From 6821bfd6658a273679a2252519fedbba11a41160 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 24 Nov 2022 14:50:34 -0800 Subject: [PATCH 01/93] bugfix: don't publish transaction if it wasn't committed to the database --- src/EntityDb.Common/Entities/EntityRepository.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EntityDb.Common/Entities/EntityRepository.cs b/src/EntityDb.Common/Entities/EntityRepository.cs index a7db31fd..e3448fc9 100644 --- a/src/EntityDb.Common/Entities/EntityRepository.cs +++ b/src/EntityDb.Common/Entities/EntityRepository.cs @@ -57,14 +57,14 @@ public async Task GetSnapshot(Pointer entityPointer, CancellationToken public async Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default) { - try - { - return await TransactionRepository.PutTransaction(transaction, cancellationToken); - } - finally + var success = await TransactionRepository.PutTransaction(transaction, cancellationToken); + + if (success) { Publish(transaction); } + + return success; } public override async ValueTask DisposeAsync() From a6fd6fe60f7096c0d609ed6adb68f2565ced5a40 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 20 Nov 2022 15:24:07 -0800 Subject: [PATCH 02/93] feat: EntityFramework snapshots --- EntityDb.sln | 9 +- .../Properties/AssemblyInfo.cs | 1 + .../EntityDb.EntityFramework.csproj | 17 +++ .../Extensions/ServiceCollectionExtensions.cs | 42 ++++++ .../Predicates/PredicateBuilder.cs | 41 ++++++ .../Sessions/EntityFrameworkSession.cs | 87 +++++++++++ .../EntityFrameworkSnapshotSessionOptions.cs | 20 +++ .../Sessions/IEntityFrameworkSession.cs | 12 ++ .../EntityFrameworkSnapshotRepository.cs | 35 +++++ ...ntityFrameworkSnapshotRepositoryFactory.cs | 65 +++++++++ .../Snapshots/ISnapshotDbContext.cs | 21 +++ .../Snapshots/SnapshotReference.cs | 28 ++++ .../SnapshotReferenceTypeConfiguration.cs | 38 +++++ .../packages.lock.json | 137 ++++++++++++++++++ 14 files changed, 552 insertions(+), 1 deletion(-) create mode 100644 src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj create mode 100644 src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs create mode 100644 src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs create mode 100644 src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs create mode 100644 src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs create mode 100644 src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs create mode 100644 src/EntityDb.EntityFramework/packages.lock.json diff --git a/EntityDb.sln b/EntityDb.sln index 3b2837a0..3f73be88 100644 --- a/EntityDb.sln +++ b/EntityDb.sln @@ -47,7 +47,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.SqlDb", "src\Entit EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Json", "src\EntityDb.Json\EntityDb.Json.csproj", "{4936FFE0-98E5-43A2-89C9-0415A13CAA9B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityDb.Provisioner", "src\EntityDb.Provisioner\EntityDb.Provisioner.csproj", "{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Provisioner", "src\EntityDb.Provisioner\EntityDb.Provisioner.csproj", "{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.EntityFramework", "src\EntityDb.EntityFramework\EntityDb.EntityFramework.csproj", "{199606BF-6283-4684-A224-4DA7E80D8F45}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -115,6 +117,10 @@ Global {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.Build.0 = Release|Any CPU + {199606BF-6283-4684-A224-4DA7E80D8F45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {199606BF-6283-4684-A224-4DA7E80D8F45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {199606BF-6283-4684-A224-4DA7E80D8F45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {199606BF-6283-4684-A224-4DA7E80D8F45}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -135,6 +141,7 @@ Global {F2491666-31D1-47B5-A493-F25E167D1FDF} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {4936FFE0-98E5-43A2-89C9-0415A13CAA9B} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} + {199606BF-6283-4684-A224-4DA7E80D8F45} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E9D288EE-9351-4018-ABE8-B0968AEB0465} diff --git a/src/EntityDb.Common/Properties/AssemblyInfo.cs b/src/EntityDb.Common/Properties/AssemblyInfo.cs index 4061b50f..912c98c8 100644 --- a/src/EntityDb.Common/Properties/AssemblyInfo.cs +++ b/src/EntityDb.Common/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ // src [assembly: InternalsVisibleTo("EntityDb.SqlDb")] [assembly: InternalsVisibleTo("EntityDb.Npgsql")] +[assembly: InternalsVisibleTo("EntityDb.EntityFramework")] [assembly: InternalsVisibleTo("EntityDb.InMemory")] [assembly: InternalsVisibleTo("EntityDb.MongoDb")] [assembly: InternalsVisibleTo("EntityDb.Provisioner")] diff --git a/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj b/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj new file mode 100644 index 00000000..2ec2f457 --- /dev/null +++ b/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj @@ -0,0 +1,17 @@ + + + + + EntityDb EventSourcing DDD CQRS + An implementation of the EntityDb Snapshot Repository interface, specifically for Entity Framework. + + + + + + + + + + + diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..7de51bce --- /dev/null +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,42 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Common.Extensions; +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.EntityFramework.Extensions; + +/// +/// Extensions for service collections. +/// +[ExcludeFromCodeCoverage(Justification = "Don't need coverage for non-test mode.")] +public static class ServiceCollectionExtensions +{ + /// + /// Adds a production-ready implementation of to a service + /// collection. + /// + /// The type of the snapshot stored in the repository. + /// The type of the snapshot stored in the repository. + /// The service collection. + /// Modifies the behavior of the repository to accomodate tests. + public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) + where TSnapshot : class + where TDbContext : DbContext, ISnapshotDbContext + { + serviceCollection.Add> + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient + ); + + serviceCollection.Add + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, + serviceProvider => serviceProvider + .GetRequiredService>() + .UseTestMode(testMode) + ); + } +} diff --git a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs b/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs new file mode 100644 index 00000000..244d0de0 --- /dev/null +++ b/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs @@ -0,0 +1,41 @@ +using EntityDb.EntityFramework.Snapshots; +using System.Linq.Expressions; + +namespace EntityDb.EntityFramework.Predicates; + +/// +/// Based on http://www.albahari.com/nutshell/predicatebuilder.aspx +/// +internal static class PredicateExpressionBuilder +{ + private static Expression> False() + { + return _ => false; + } + + private static Expression> Or + ( + Expression> left, + Expression> right + ) + { + return Expression.Lambda> + ( + Expression.OrElse + ( + left.Body, + Expression.Invoke(right, left.Parameters) + ), + left.Parameters + ); + } + + public static Expression> Or + ( + IEnumerable inputs, + Func>> mapper + ) + { + return inputs.Aggregate(False(), (predicate, input) => Or(predicate, mapper.Invoke(input))); + } +} diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs new file mode 100644 index 00000000..4436b93a --- /dev/null +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -0,0 +1,87 @@ +using EntityDb.Common.Disposables; +using EntityDb.Common.Exceptions; +using EntityDb.EntityFramework.Predicates; +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System.Linq.Expressions; +using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; + +namespace EntityDb.EntityFramework.Sessions; + +internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession + where TSnapshot : class + where TDbContext : DbContext, ISnapshotDbContext +{ + private readonly TDbContext _dbContext; + private readonly EntityFrameworkSnapshotSessionOptions _options; + + public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessionOptions options) + { + _dbContext = dbContext; + _options = options; + } + + private static Expression, bool>> SnapshotPointerPredicate(Pointer snapshotPointer) + { + return snapshotReference => + snapshotReference.PointerId == snapshotPointer.Id.Value && + snapshotReference.PointerVersionNumber == snapshotPointer.VersionNumber.Value; + } + + public async Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken) + { + AssertNotReadOnly(); + + await _dbContext.SnapshotReferences + .Where(PredicateExpressionBuilder.Or(snapshotPointers, SnapshotPointerPredicate)) + .ExecuteDeleteAsync(cancellationToken); + } + + public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) + { + var reference = await _dbContext.SnapshotReferences + .Where(SnapshotPointerPredicate(snapshotPointer)) + .AsNoTracking() + .SingleOrDefaultAsync(cancellationToken); + + var test = await _dbContext.Snapshots.AllAsync(_ => true, cancellationToken); + + return reference?.Snapshot; + } + + public async Task Insert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) + { + AssertNotReadOnly(); + + var reference = new SnapshotReference + { + Id = Guid.NewGuid(), + PointerId = snapshotPointer.Id.Value, + PointerVersionNumber = snapshotPointer.VersionNumber.Value, + Snapshot = snapshot + }; + + _dbContext.SnapshotReferences.Add(reference); + + await _dbContext.SaveChangesAsync(cancellationToken); + } + + private void AssertNotReadOnly() + { + if (_options.ReadOnly) + { + throw new CannotWriteInReadOnlyModeException(); + } + } + + public static IEntityFrameworkSession Create + ( + IServiceProvider serviceProvider, + TDbContext dbContext, + EntityFrameworkSnapshotSessionOptions options + ) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, dbContext, options); + } +} diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs new file mode 100644 index 00000000..6ca17245 --- /dev/null +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Snapshots; + +namespace EntityDb.EntityFramework.Sessions; + +/// +/// Configuration options for the Redis implementation of . +/// +public class EntityFrameworkSnapshotSessionOptions +{ + /// + /// If true, indicates the agent only intends to execute queries. + /// + public bool ReadOnly { get; set; } + + /// + public override string ToString() + { + return $"{nameof(EntityFrameworkSnapshotSessionOptions)}"; + } +} diff --git a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs new file mode 100644 index 00000000..62dca2ee --- /dev/null +++ b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.EntityFramework.Sessions; + +internal interface IEntityFrameworkSession +{ + Task Insert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken); + + Task Get(Pointer snapshotPointer, CancellationToken cancellationToken); + + Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken); +} diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs new file mode 100644 index 00000000..5c2e3914 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs @@ -0,0 +1,35 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.EntityFramework.Sessions; + +namespace EntityDb.EntityFramework.Snapshots; + +internal class EntityFrameworkSnapshotRepository : DisposableResourceBaseClass, ISnapshotRepository +{ + private readonly IEntityFrameworkSession _entityFrameworkSession; + + public EntityFrameworkSnapshotRepository(IEntityFrameworkSession entityFrameworkSession) + { + _entityFrameworkSession = entityFrameworkSession; + } + + public async Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) + { + await _entityFrameworkSession.Delete(snapshotPointers, cancellationToken); + + return true; + } + + public Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default) + { + return _entityFrameworkSession.Get(snapshotPointer, cancellationToken); + } + + public async Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken = default) + { + await _entityFrameworkSession.Insert(snapshotPointer, snapshot, cancellationToken); + + return true; + } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs new file mode 100644 index 00000000..84624f7e --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -0,0 +1,65 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Common.Disposables; +using EntityDb.Common.Snapshots; +using EntityDb.EntityFramework.Sessions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace EntityDb.EntityFramework.Snapshots; + +internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, + ISnapshotRepositoryFactory + where TSnapshot : class + where TDbContext : DbContext, ISnapshotDbContext +{ + private readonly IServiceProvider _serviceProvider; + private readonly IDbContextFactory _dbContextFactory; + private readonly IOptionsFactory _optionsFactory; + + public EntityFrameworkSnapshotRepositoryFactory + ( + IServiceProvider serviceProvider, + IDbContextFactory dbContextFactory, + IOptionsFactory optionsFactory + ) + { + _serviceProvider = serviceProvider; + _dbContextFactory = dbContextFactory; + _optionsFactory = optionsFactory; + } + + public async Task> CreateRepository(string snapshotSessionOptionsName, + CancellationToken cancellationToken = default) + { + var options = _optionsFactory.Create(snapshotSessionOptionsName); + + var entityFrameworkSession = await CreateSession(options, cancellationToken); + + var entityFrameworkSnapshotRepository = new EntityFrameworkSnapshotRepository + ( + entityFrameworkSession + ); + + return TryCatchSnapshotRepository.Create(_serviceProvider, entityFrameworkSnapshotRepository); + } + + private async Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, + CancellationToken cancellationToken) + { + var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); + + return EntityFrameworkSession.Create(_serviceProvider, dbContext, options); + } + + public static EntityFrameworkSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, + string connectionString, string keyNamespace) + { + return ActivatorUtilities.CreateInstance> + ( + serviceProvider, + connectionString, + keyNamespace + ); + } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs new file mode 100644 index 00000000..2d2ba754 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; + +namespace EntityDb.EntityFramework.Snapshots; + +/// +/// The base DbContext +/// +/// The type of the snapshot +public interface ISnapshotDbContext + where TSnapshot : class +{ + /// + /// A database set for resolving specific snapshots from pointers. + /// + DbSet> SnapshotReferences { get; set; } + + /// + /// A database set for the root snapshots. + /// + DbSet Snapshots { get; set; } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs new file mode 100644 index 00000000..572b6dc0 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs @@ -0,0 +1,28 @@ +namespace EntityDb.EntityFramework.Snapshots; + +/// +/// Represents a unique snapshot and its pointer. +/// +/// +public class SnapshotReference +{ + /// + /// Te ID of the Reference record. + /// + public required Guid Id { get; init; } + + /// + /// The ID of the Snapshot Pointer. + /// + public required Guid PointerId { get; init; } + + /// + /// The Version Number of the Snapshot Pointer. + /// + public required ulong PointerVersionNumber { get; init; } + + /// + /// The Snapshot. + /// + public required TSnapshot Snapshot { get; init; } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs new file mode 100644 index 00000000..200b1173 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace EntityDb.EntityFramework.Snapshots; + +/// +public class SnapshotReferenceTypeConfiguration : IEntityTypeConfiguration> + where TSnapshot : class +{ + private readonly string _tableName = $"{typeof(TSnapshot).Name}SnapshotReferences"; + + /// + public void Configure(EntityTypeBuilder> snapshotReferenceBuilder) + { + snapshotReferenceBuilder + .ToTable(_tableName); + + snapshotReferenceBuilder + .HasKey + ( + nameof(SnapshotReference.Id) + ); + + snapshotReferenceBuilder + .HasAlternateKey + ( + nameof(SnapshotReference.PointerId), + nameof(SnapshotReference.PointerVersionNumber) + ); + + snapshotReferenceBuilder + .Navigation(snapshotReference => snapshotReference.Snapshot) + .AutoInclude(); + + snapshotReferenceBuilder + .HasOne(snapshotReference => snapshotReference.Snapshot); + } +} diff --git a/src/EntityDb.EntityFramework/packages.lock.json b/src/EntityDb.EntityFramework/packages.lock.json new file mode 100644 index 00000000..74b6287f --- /dev/null +++ b/src/EntityDb.EntityFramework/packages.lock.json @@ -0,0 +1,137 @@ +{ + "version": 1, + "dependencies": { + "net7.0": { + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.Extensions.Caching.Memory": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.Logging": "7.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } + } + } +} \ No newline at end of file From 74c1cc7d28a23b935282d6f194c2023824870a4d Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 20 Nov 2022 15:24:16 -0800 Subject: [PATCH 03/93] test: EntityFramework snapshots --- .../EntityDb.Common.Tests.csproj | 2 + .../Implementations/Entities/TestEntity.cs | 25 +++- .../Projections/OneToOneProjection.cs | 29 ++++- .../Snapshots/GenericDbContext.cs | 34 +++++ .../Snapshots/ISnapshotWithTestLogic.cs | 3 + .../Snapshots/SnapshotTypeConfiguration.cs | 23 ++++ test/EntityDb.Common.Tests/TestsBase.cs | 42 +++++- test/EntityDb.Common.Tests/packages.lock.json | 120 +++++++++++++---- .../EntityDb.MongoDb.Tests/packages.lock.json | 121 ++++++++++++++---- test/EntityDb.Mvc.Tests/packages.lock.json | 121 ++++++++++++++---- test/EntityDb.Redis.Tests/packages.lock.json | 121 ++++++++++++++---- 11 files changed, 522 insertions(+), 119 deletions(-) create mode 100644 test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs diff --git a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj index 0dc9cf41..47634e17 100644 --- a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj +++ b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj @@ -2,12 +2,14 @@ + + diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 72a8cd6d..9a5390cd 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,4 +1,5 @@ -using EntityDb.Abstractions.ValueObjects; +using System.ComponentModel.DataAnnotations.Schema; +using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Commands; using EntityDb.Common.Tests.Implementations.Snapshots; @@ -7,11 +8,29 @@ namespace EntityDb.Common.Tests.Implementations.Entities; public record TestEntity ( - Id Id, - VersionNumber VersionNumber = default + [property:NotMapped] Id Id, + [property:NotMapped] VersionNumber VersionNumber = default ) : IEntity, ISnapshotWithTestLogic { + [Column("EntityId")] + public Guid IdValue + { + get => Id.Value; + init => Id = new Id(value); + } + + [Column("EntityVersionNumber")] + public ulong VersionNumberValue + { + get => VersionNumber.Value; + init => VersionNumber = new VersionNumber(value); + } + + public TestEntity() : this(default(Id)) + { + } + public static TestEntity Construct(Id entityId) { return new TestEntity(entityId); diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 8a3cd23b..a500dbe9 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations.Schema; using EntityDb.Abstractions.Annotations; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Reducers; @@ -6,16 +7,34 @@ using EntityDb.Common.Queries; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; +using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; namespace EntityDb.Common.Tests.Implementations.Projections; public record OneToOneProjection ( - Id Id, - VersionNumber VersionNumber = default, - VersionNumber EntityVersionNumber = default + [property: NotMapped] Id Id, + [property: NotMapped] VersionNumber VersionNumber = default ) : IProjection, ISnapshotWithTestLogic { + [Column("Id")] + public Guid IdValue + { + get => Id.Value; + init => Id = new Id(value); + } + + [Column("VersionNumber")] + public ulong VersionNumberValue + { + get => VersionNumber.Value; + init => VersionNumber = new VersionNumber(value); + } + + public OneToOneProjection() : this(default(Id)) + { + } + public static OneToOneProjection Construct(Id projectionId) { return new OneToOneProjection(projectionId); @@ -37,7 +56,7 @@ public OneToOneProjection Reduce(IEntityAnnotation annotatedCommand) { IEntityAnnotation> reducer => reducer.Data.Reduce(this) with { - EntityVersionNumber = reducer.EntityVersionNumber + VersionNumber = reducer.EntityVersionNumber }, _ => throw new NotSupportedException() }; @@ -55,7 +74,7 @@ public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) public ICommandQuery GetCommandQuery(Pointer projectionPointer) { - return new GetEntityCommandsQuery(projectionPointer, EntityVersionNumber); + return new GetEntityCommandsQuery(projectionPointer, VersionNumber); } public static Id? GetProjectionIdOrDefault(object entity) diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs new file mode 100644 index 00000000..0e8a8407 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs @@ -0,0 +1,34 @@ +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; + +namespace EntityDb.Common.Tests.Implementations.Snapshots; + +internal class SnapshotDbContext : DbContext, ISnapshotDbContext + where TSnapshot : class, ISnapshotWithTestLogic +{ + public DbSet> SnapshotReferences { get; set; } = default!; + public DbSet Snapshots { get; set; } + + public SnapshotDbContext(DbContextOptions> options) : base(options) + { + try + { + var databaseCreator = (RelationalDatabaseCreator)Database.GetService(); + + databaseCreator.CreateTables(); + } + catch + { + // One of the tests has already created the tables. + } + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder + .ApplyConfiguration(new SnapshotReferenceTypeConfiguration()) + .ApplyConfiguration(new SnapshotTypeConfiguration()); + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs index ac669003..5dab0f71 100644 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs @@ -5,6 +5,9 @@ namespace EntityDb.Common.Tests.Implementations.Snapshots; public interface ISnapshotWithTestLogic : ISnapshot { + Guid IdValue { get; } + ulong VersionNumberValue { get; } + VersionNumber VersionNumber { get; } static abstract string RedisKeyNamespace { get; } static abstract AsyncLocal?> ShouldRecordLogic { get; } diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs new file mode 100644 index 00000000..8a6d9b3e --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace EntityDb.Common.Tests.Implementations.Snapshots; + +public class SnapshotTypeConfiguration : IEntityTypeConfiguration + where TSnapshot : class, ISnapshotWithTestLogic +{ + private readonly string _tableName = $"{typeof(TSnapshot).Name}s"; + + public void Configure(EntityTypeBuilder snapshotBuilder) + { + snapshotBuilder + .ToTable(_tableName); + + snapshotBuilder + .HasKey + ( + nameof(ISnapshotWithTestLogic.IdValue), + nameof(ISnapshotWithTestLogic.VersionNumberValue) + ); + } +} diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index b9d04b78..b2f16f9c 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -11,6 +11,8 @@ using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.Extensions; +using EntityDb.EntityFramework.Sessions; using EntityDb.InMemory.Extensions; using EntityDb.InMemory.Sessions; using EntityDb.MongoDb.Extensions; @@ -21,6 +23,7 @@ using EntityDb.Redis.Extensions; using EntityDb.Redis.Sessions; using EntityDb.SqlDb.Sessions; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -150,6 +153,38 @@ protected Task RunGenericTestAsync(Type[] typeArguments, object?[] invokeParamet .ShouldNotBeNull(); } + private static SnapshotAdder EntityFrameworkSnapshotAdder() + where TSnapshot : class, ISnapshotWithTestLogic + { + return new SnapshotAdder($"EntityFramework<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => + { + var databaseContainerFixture = serviceCollection + .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) + .ImplementationInstance as DatabaseContainerFixture; + + serviceCollection.AddDbContextFactory>(options => options + .UseNpgsql($"{databaseContainerFixture!.PostgreSqlContainer.ConnectionString};Include Error Detail=true") + .EnableSensitiveDataLogging()); + + serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); + + serviceCollection.Configure(TestSessionOptions.Write, options => + { + options.ReadOnly = false; + }); + + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => + { + options.ReadOnly = true; + }); + + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => + { + options.ReadOnly = true; + }); + }); + } + private static SnapshotAdder RedisSnapshotAdder() where TSnapshot : ISnapshotWithTestLogic { @@ -211,8 +246,9 @@ private static SnapshotAdder InMemorySnapshotAdder() } private static IEnumerable AllSnapshotAdders() - where TSnapshot : ISnapshotWithTestLogic + where TSnapshot : class, ISnapshotWithTestLogic { + yield return EntityFrameworkSnapshotAdder(); yield return RedisSnapshotAdder(); yield return InMemorySnapshotAdder(); } @@ -225,7 +261,7 @@ private static EntityAdder GetEntityAdder() } private static IEnumerable AllEntitySnapshotAdders() - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { return from snapshotAdder in AllSnapshotAdders() @@ -250,7 +286,7 @@ private static IEnumerable AllEntitySnapshotAdders() } private static IEnumerable AllProjectionAdders() - where TProjection : IProjection, ISnapshotWithTestLogic + where TProjection : class, IProjection, ISnapshotWithTestLogic { return AllSnapshotAdders() .Select(snapshotAdder => new SnapshotAdder(snapshotAdder.Name, snapshotAdder.SnapshotType, diff --git a/test/EntityDb.Common.Tests/packages.lock.json b/test/EntityDb.Common.Tests/packages.lock.json index 1b7f9f5f..48a2f446 100644 --- a/test/EntityDb.Common.Tests/packages.lock.json +++ b/test/EntityDb.Common.Tests/packages.lock.json @@ -33,6 +33,18 @@ "Castle.Core": "5.1.0" } }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "Direct", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", + "Npgsql": "7.0.0" + } + }, "Shouldly": { "type": "Direct", "requested": "[4.1.0, )", @@ -169,6 +181,57 @@ "resolved": "4.7.0", "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.Extensions.Caching.Memory": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.Logging": "7.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -180,10 +243,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "7.0.0", + "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -249,17 +312,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -324,20 +386,19 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -405,11 +466,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -426,11 +487,8 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -1636,6 +1694,14 @@ "System.Linq.Async": "[6.0.1, )" } }, + "entitydb.entityframework": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, "entitydb.inmemory": { "type": "Project", "dependencies": { diff --git a/test/EntityDb.MongoDb.Tests/packages.lock.json b/test/EntityDb.MongoDb.Tests/packages.lock.json index bd797557..ddbc98af 100644 --- a/test/EntityDb.MongoDb.Tests/packages.lock.json +++ b/test/EntityDb.MongoDb.Tests/packages.lock.json @@ -154,6 +154,57 @@ "resolved": "4.7.0", "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.Extensions.Caching.Memory": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.Logging": "7.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -165,10 +216,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "7.0.0", + "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -234,17 +285,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -309,20 +359,19 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -390,11 +439,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -411,11 +460,8 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -576,6 +622,17 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", + "Npgsql": "7.0.0" + } + }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "5.11.0", @@ -1640,11 +1697,13 @@ "dependencies": { "Bogus": "[34.0.2, )", "EntityDb.Common": "[1.0.0, )", + "EntityDb.EntityFramework": "[1.0.0, )", "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", "Microsoft.NET.Test.Sdk": "[17.4.0, )", "Moq": "[4.18.2, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.0, )", "Shouldly": "[4.1.0, )", "System.Linq.Async": "[6.0.1, )", "Testcontainers": "[2.2.0, )", @@ -1653,6 +1712,14 @@ "xunit": "[2.4.2, )" } }, + "entitydb.entityframework": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, "entitydb.inmemory": { "type": "Project", "dependencies": { diff --git a/test/EntityDb.Mvc.Tests/packages.lock.json b/test/EntityDb.Mvc.Tests/packages.lock.json index fbecc27c..866d07aa 100644 --- a/test/EntityDb.Mvc.Tests/packages.lock.json +++ b/test/EntityDb.Mvc.Tests/packages.lock.json @@ -154,6 +154,57 @@ "resolved": "4.7.0", "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.Extensions.Caching.Memory": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.Logging": "7.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -165,10 +216,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "7.0.0", + "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -234,17 +285,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -309,20 +359,19 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -390,11 +439,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -411,11 +460,8 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -576,6 +622,17 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", + "Npgsql": "7.0.0" + } + }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "5.11.0", @@ -1640,11 +1697,13 @@ "dependencies": { "Bogus": "[34.0.2, )", "EntityDb.Common": "[1.0.0, )", + "EntityDb.EntityFramework": "[1.0.0, )", "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", "Microsoft.NET.Test.Sdk": "[17.4.0, )", "Moq": "[4.18.2, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.0, )", "Shouldly": "[4.1.0, )", "System.Linq.Async": "[6.0.1, )", "Testcontainers": "[2.2.0, )", @@ -1653,6 +1712,14 @@ "xunit": "[2.4.2, )" } }, + "entitydb.entityframework": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, "entitydb.inmemory": { "type": "Project", "dependencies": { diff --git a/test/EntityDb.Redis.Tests/packages.lock.json b/test/EntityDb.Redis.Tests/packages.lock.json index bd797557..ddbc98af 100644 --- a/test/EntityDb.Redis.Tests/packages.lock.json +++ b/test/EntityDb.Redis.Tests/packages.lock.json @@ -154,6 +154,57 @@ "resolved": "4.7.0", "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.Extensions.Caching.Memory": "7.0.0", + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.Logging": "7.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" + } + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -165,10 +216,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "7.0.0", + "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -234,17 +285,16 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "7.0.0", + "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "7.0.0", + "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -309,20 +359,19 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "7.0.0", + "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "7.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Logging.Abstractions": "7.0.0", + "Microsoft.Extensions.Options": "7.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + "resolved": "7.0.0", + "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -390,11 +439,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.0.0", + "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", + "Microsoft.Extensions.Primitives": "7.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -411,11 +460,8 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "7.0.0", + "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -576,6 +622,17 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", + "Npgsql": "7.0.0" + } + }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "5.11.0", @@ -1640,11 +1697,13 @@ "dependencies": { "Bogus": "[34.0.2, )", "EntityDb.Common": "[1.0.0, )", + "EntityDb.EntityFramework": "[1.0.0, )", "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", "Microsoft.NET.Test.Sdk": "[17.4.0, )", "Moq": "[4.18.2, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.0, )", "Shouldly": "[4.1.0, )", "System.Linq.Async": "[6.0.1, )", "Testcontainers": "[2.2.0, )", @@ -1653,6 +1712,14 @@ "xunit": "[2.4.2, )" } }, + "entitydb.entityframework": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, "entitydb.inmemory": { "type": "Project", "dependencies": { From bd91eae6b6be68e3162cebd3415501b9a4ffc7af Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 24 Nov 2022 20:47:57 -0800 Subject: [PATCH 04/93] fix: remove test code --- src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 4436b93a..d4945b7f 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -45,8 +45,6 @@ await _dbContext.SnapshotReferences .AsNoTracking() .SingleOrDefaultAsync(cancellationToken); - var test = await _dbContext.Snapshots.AllAsync(_ => true, cancellationToken); - return reference?.Snapshot; } From 6292a7ac35a425065ff70018860737391acfca34 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 24 Nov 2022 21:47:51 -0800 Subject: [PATCH 05/93] refactor: provide a dbContext which handles the boilerplate --- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../Sessions/EntityFrameworkSession.cs | 2 +- ...ntityFrameworkSnapshotRepositoryFactory.cs | 2 +- .../Snapshots/ISnapshotDbContext.cs | 21 ------------- .../Snapshots/SnapshotReferenceDbContext.cs | 30 +++++++++++++++++++ .../SnapshotReferenceTypeConfiguration.cs | 23 +++++++------- 6 files changed, 45 insertions(+), 35 deletions(-) delete mode 100644 src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index 7de51bce..531fd96f 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -24,7 +24,7 @@ public static class ServiceCollectionExtensions /// Modifies the behavior of the repository to accomodate tests. public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) where TSnapshot : class - where TDbContext : DbContext, ISnapshotDbContext + where TDbContext : SnapshotReferenceDbContext { serviceCollection.Add> ( diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index d4945b7f..7c52ffa1 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -11,7 +11,7 @@ namespace EntityDb.EntityFramework.Sessions; internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession where TSnapshot : class - where TDbContext : DbContext, ISnapshotDbContext + where TDbContext : SnapshotReferenceDbContext { private readonly TDbContext _dbContext; private readonly EntityFrameworkSnapshotSessionOptions _options; diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index 84624f7e..fc6ba80e 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -11,7 +11,7 @@ namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, ISnapshotRepositoryFactory where TSnapshot : class - where TDbContext : DbContext, ISnapshotDbContext + where TDbContext : SnapshotReferenceDbContext { private readonly IServiceProvider _serviceProvider; private readonly IDbContextFactory _dbContextFactory; diff --git a/src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs deleted file mode 100644 index 2d2ba754..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/ISnapshotDbContext.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace EntityDb.EntityFramework.Snapshots; - -/// -/// The base DbContext -/// -/// The type of the snapshot -public interface ISnapshotDbContext - where TSnapshot : class -{ - /// - /// A database set for resolving specific snapshots from pointers. - /// - DbSet> SnapshotReferences { get; set; } - - /// - /// A database set for the root snapshots. - /// - DbSet Snapshots { get; set; } -} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs new file mode 100644 index 00000000..61e4f042 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; + +namespace EntityDb.EntityFramework.Snapshots; + +/// +/// A minimal DbContext for snapshot references. +/// +/// The type of the snapshot +public abstract class SnapshotReferenceDbContext : DbContext + where TSnapshot : class +{ + /// + /// A database set for resolving snapshots from snapshot pointers. + /// + public required DbSet> SnapshotReferences { get; set; } + + /// + protected SnapshotReferenceDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + + /// + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder + .ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); + } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs index 200b1173..e6696a69 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -7,14 +7,9 @@ namespace EntityDb.EntityFramework.Snapshots; public class SnapshotReferenceTypeConfiguration : IEntityTypeConfiguration> where TSnapshot : class { - private readonly string _tableName = $"{typeof(TSnapshot).Name}SnapshotReferences"; - /// - public void Configure(EntityTypeBuilder> snapshotReferenceBuilder) + public virtual void Configure(EntityTypeBuilder> snapshotReferenceBuilder) { - snapshotReferenceBuilder - .ToTable(_tableName); - snapshotReferenceBuilder .HasKey ( @@ -28,11 +23,17 @@ public void Configure(EntityTypeBuilder> snapshotRe nameof(SnapshotReference.PointerVersionNumber) ); - snapshotReferenceBuilder - .Navigation(snapshotReference => snapshotReference.Snapshot) - .AutoInclude(); + var snapshotBuilder = snapshotReferenceBuilder + .OwnsOne(snapshotReference => snapshotReference.Snapshot); - snapshotReferenceBuilder - .HasOne(snapshotReference => snapshotReference.Snapshot); + Configure(snapshotBuilder); + } + + /// + /// Configures the snapshot of type . + /// + /// The builder to be used to configure the snapshot type. + protected virtual void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder) + { } } From 7bb785ec09601e8a93b21f7fa2e2ea98b05f591f Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 24 Nov 2022 21:48:23 -0800 Subject: [PATCH 06/93] fix(test): update tests to match new structure --- .../Entities/EntityTests.cs | 16 +++--- .../DbContexts/GenericDbContext.cs | 53 +++++++++++++++++++ .../Implementations/Entities/TestEntity.cs | 36 +++++++++---- .../Projections/OneToOneProjection.cs | 30 ++++++++--- .../Seeders/TransactionSeeder.cs | 4 +- .../Snapshots/GenericDbContext.cs | 34 ------------ .../Snapshots/ISnapshotWithTestLogic.cs | 7 +-- .../Snapshots/SnapshotTypeConfiguration.cs | 23 -------- .../Projections/ProjectionsTests.cs | 4 +- .../Snapshots/SnapshotTests.cs | 6 +-- test/EntityDb.Common.Tests/TestsBase.cs | 9 ++-- ...ntitySnapshotTransactionSubscriberTests.cs | 4 +- .../Transactions/TransactionTests.cs | 2 +- 13 files changed, 127 insertions(+), 101 deletions(-) create mode 100644 test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 0a544d14..3e6bb5cb 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -32,7 +32,7 @@ private static async Task BuildTransaction VersionNumber to, TEntity? entity = default ) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { var transactionBuilder = await serviceScope.ServiceProvider .GetRequiredService>() @@ -54,7 +54,7 @@ private static async Task BuildTransaction private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -97,7 +97,7 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetCommandsRuns( EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -172,7 +172,7 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T private async Task Generic_GivenNoSnapshotRepositoryFactory_WhenCreatingEntityRepository_ThenNoSnapshotRepository( EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -202,7 +202,7 @@ private async Task private async Task Generic_GivenNoSnapshotSessionOptions_WhenCreatingEntityRepository_ThenNoSnapshotRepository( EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -233,7 +233,7 @@ private async Task private async Task Generic_GivenSnapshotRepositoryFactoryAndSnapshotSessionOptions_WhenCreatingEntityRepository_ThenNoSnapshotRepository< TEntity>(EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -264,7 +264,7 @@ private async Task private async Task Generic_GivenSnapshotAndNewCommands_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -302,7 +302,7 @@ private async Task private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow( EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs new file mode 100644 index 00000000..6a182b3b --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -0,0 +1,53 @@ +using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage; + +namespace EntityDb.Common.Tests.Implementations.DbContexts; + +internal class GenericDbContext : SnapshotReferenceDbContext + where TSnapshot : class, ISnapshotWithTestLogic +{ + public GenericDbContext(DbContextOptions> options) : base(options) + { + try + { + var databaseCreator = (RelationalDatabaseCreator)Database.GetService(); + + databaseCreator.CreateTables(); + } + catch + { + // One of the tests has already created the tables. + } + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder + .ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); + } +} + + +public class SnapshotReferenceTypeConfiguration : EntityFramework.Snapshots.SnapshotReferenceTypeConfiguration + where TSnapshot : class, ISnapshotWithTestLogic +{ + public override void Configure(EntityTypeBuilder> snapshotReferenceBuilder) + { + base.Configure(snapshotReferenceBuilder); + + snapshotReferenceBuilder.ToTable($"{typeof(TSnapshot).Name}SnapshotReferences"); + } + + protected override void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder) + { + base.Configure(snapshotBuilder); + + snapshotBuilder.ToTable($"{typeof(TSnapshot).Name}Snapshots"); + + TSnapshot.Configure(snapshotBuilder); + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 9a5390cd..2c13e24f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -2,38 +2,52 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Commands; +using EntityDb.Common.Tests.Implementations.Projections; using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace EntityDb.Common.Tests.Implementations.Entities; -public record TestEntity - ( - [property:NotMapped] Id Id, - [property:NotMapped] VersionNumber VersionNumber = default - ) - : IEntity, ISnapshotWithTestLogic +public record TestEntity : IEntity, ISnapshotWithTestLogic { - [Column("EntityId")] + public required Id Id { get; init; } + public VersionNumber VersionNumber { get; init; } + + [Column("Id")] public Guid IdValue { get => Id.Value; init => Id = new Id(value); } - [Column("EntityVersionNumber")] + [Column("VersionNumber")] public ulong VersionNumberValue { get => VersionNumber.Value; init => VersionNumber = new VersionNumber(value); } - public TestEntity() : this(default(Id)) + public static TestEntity Construct(Id entityId) { + return new TestEntity + { + Id = entityId, + }; } - public static TestEntity Construct(Id entityId) + public static void Configure(OwnedNavigationBuilder, TestEntity> testEntityBuilder) { - return new TestEntity(entityId); + testEntityBuilder + .Ignore(testEntity => testEntity.Id) + .Ignore(testEntity => testEntity.VersionNumber); + + testEntityBuilder + .HasKey(testEntity => new + { + testEntity.IdValue, + testEntity.VersionNumberValue, + }); } public Id GetId() diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index a500dbe9..e6eb6997 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -7,16 +7,17 @@ using EntityDb.Common.Queries; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; namespace EntityDb.Common.Tests.Implementations.Projections; -public record OneToOneProjection -( - [property: NotMapped] Id Id, - [property: NotMapped] VersionNumber VersionNumber = default -) : IProjection, ISnapshotWithTestLogic +public record OneToOneProjection : IProjection, ISnapshotWithTestLogic { + public required Id Id { get; init; } + public VersionNumber VersionNumber { get; init; } + [Column("Id")] public Guid IdValue { @@ -31,13 +32,26 @@ public ulong VersionNumberValue init => VersionNumber = new VersionNumber(value); } - public OneToOneProjection() : this(default(Id)) + public static OneToOneProjection Construct(Id projectionId) { + return new OneToOneProjection + { + Id = projectionId, + }; } - public static OneToOneProjection Construct(Id projectionId) + public static void Configure(OwnedNavigationBuilder, OneToOneProjection> oneToOneProjectionBuilder) { - return new OneToOneProjection(projectionId); + oneToOneProjectionBuilder + .Ignore(oneToOneProjection => oneToOneProjection.Id) + .Ignore(oneToOneProjection => oneToOneProjection.VersionNumber); + + oneToOneProjectionBuilder + .HasKey(oneToOneProjection => new + { + oneToOneProjection.IdValue, + oneToOneProjection.VersionNumberValue, + }); } public Id GetId() diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs index a99939b2..93c036b2 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs @@ -13,7 +13,7 @@ namespace EntityDb.Common.Tests.Implementations.Seeders; public static class TransactionStepSeeder { public static IEnumerable CreateFromCommands(Id entityId, uint numCommands) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { for (var previousVersionNumber = new VersionNumber(0); previousVersionNumber.Value < numCommands; @@ -47,7 +47,7 @@ public static ITransaction Create(params ITransactionStep[] transactionSteps) } public static ITransaction Create(Id entityId, uint numCommands) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { var transactionSteps = TransactionStepSeeder.CreateFromCommands(entityId, numCommands).ToArray(); diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs deleted file mode 100644 index 0e8a8407..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/GenericDbContext.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.EntityFramework.Snapshots; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; - -namespace EntityDb.Common.Tests.Implementations.Snapshots; - -internal class SnapshotDbContext : DbContext, ISnapshotDbContext - where TSnapshot : class, ISnapshotWithTestLogic -{ - public DbSet> SnapshotReferences { get; set; } = default!; - public DbSet Snapshots { get; set; } - - public SnapshotDbContext(DbContextOptions> options) : base(options) - { - try - { - var databaseCreator = (RelationalDatabaseCreator)Database.GetService(); - - databaseCreator.CreateTables(); - } - catch - { - // One of the tests has already created the tables. - } - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder - .ApplyConfiguration(new SnapshotReferenceTypeConfiguration()) - .ApplyConfiguration(new SnapshotTypeConfiguration()); - } -} diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs index 5dab0f71..b74ee380 100644 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs @@ -1,16 +1,17 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Snapshots; +using EntityDb.EntityFramework.Snapshots; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace EntityDb.Common.Tests.Implementations.Snapshots; public interface ISnapshotWithTestLogic : ISnapshot + where TSnapshot : class { - Guid IdValue { get; } - ulong VersionNumberValue { get; } - VersionNumber VersionNumber { get; } static abstract string RedisKeyNamespace { get; } static abstract AsyncLocal?> ShouldRecordLogic { get; } static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } + static abstract void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder); TSnapshot WithVersionNumber(VersionNumber versionNumber); } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs deleted file mode 100644 index 8a6d9b3e..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/SnapshotTypeConfiguration.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace EntityDb.Common.Tests.Implementations.Snapshots; - -public class SnapshotTypeConfiguration : IEntityTypeConfiguration - where TSnapshot : class, ISnapshotWithTestLogic -{ - private readonly string _tableName = $"{typeof(TSnapshot).Name}s"; - - public void Configure(EntityTypeBuilder snapshotBuilder) - { - snapshotBuilder - .ToTable(_tableName); - - snapshotBuilder - .HasKey - ( - nameof(ISnapshotWithTestLogic.IdValue), - nameof(ISnapshotWithTestLogic.VersionNumberValue) - ); - } -} diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index fad30023..a405c8b5 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -47,8 +47,8 @@ private async Task Generic_GivenTransactionCommitted_WhenGettingProjection_ThenReturnExpectedProjection( TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) - where TEntity : IEntity, ISnapshotWithTestLogic - where TProjection : IProjection, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic + where TProjection : class, IProjection, ISnapshotWithTestLogic { // ARRANGE diff --git a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs index f5fccc57..f970f81e 100644 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs @@ -21,7 +21,7 @@ public SnapshotTests(IServiceProvider startupServiceProvider, DatabaseContainerF private async Task Generic_GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInsertedMatchesFetched( SnapshotAdder snapshotAdder) - where TSnapshot : ISnapshotWithTestLogic + where TSnapshot : class, ISnapshotWithTestLogic { // ARRANGE @@ -68,7 +68,7 @@ public Task GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInse private async Task Generic_GivenEmptySnapshotRepository_WhenPuttingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged< TSnapshot>(SnapshotAdder snapshotAdder) - where TSnapshot : ISnapshotWithTestLogic + where TSnapshot : class, ISnapshotWithTestLogic { // ARRANGE @@ -116,7 +116,7 @@ public Task private async Task Generic_GivenInsertedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot( SnapshotAdder snapshotAdder) - where TSnapshot : ISnapshotWithTestLogic + where TSnapshot : class, ISnapshotWithTestLogic { // ARRANGE diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index b2f16f9c..07f074dc 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -8,6 +8,7 @@ using EntityDb.Common.Extensions; using EntityDb.Common.Polyfills; using EntityDb.Common.Projections; +using EntityDb.Common.Tests.Implementations.DbContexts; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; using EntityDb.Common.Tests.Implementations.Snapshots; @@ -162,11 +163,11 @@ private static SnapshotAdder EntityFrameworkSnapshotAdder() .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) .ImplementationInstance as DatabaseContainerFixture; - serviceCollection.AddDbContextFactory>(options => options + serviceCollection.AddDbContextFactory>(options => options .UseNpgsql($"{databaseContainerFixture!.PostgreSqlContainer.ConnectionString};Include Error Detail=true") .EnableSensitiveDataLogging()); - serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); + serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); serviceCollection.Configure(TestSessionOptions.Write, options => { @@ -186,7 +187,7 @@ private static SnapshotAdder EntityFrameworkSnapshotAdder() } private static SnapshotAdder RedisSnapshotAdder() - where TSnapshot : ISnapshotWithTestLogic + where TSnapshot : class, ISnapshotWithTestLogic { return new SnapshotAdder($"Redis<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => { @@ -222,7 +223,7 @@ private static SnapshotAdder RedisSnapshotAdder() } private static SnapshotAdder InMemorySnapshotAdder() - where TSnapshot : ISnapshotWithTestLogic + where TSnapshot : class, ISnapshotWithTestLogic { return new SnapshotAdder($"InMemory<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => { diff --git a/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs b/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs index 9edfa1ca..f4f8b5e4 100644 --- a/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs @@ -21,7 +21,7 @@ private async Task Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotTransactionSubscriber_ThenAlwaysWriteSnapshot< TEntity>( TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -75,7 +75,7 @@ private Task private async Task Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotTransactionSubscriber_ThenNeverWriteSnapshot< TEntity>(TransactionsAdder transactionsAdder, SnapshotAdder snapshotAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE diff --git a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs b/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs index c1a3c191..bb86ac11 100644 --- a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs @@ -894,7 +894,7 @@ private async Task Generic_GivenCommandInserted_WhenGettingAnnotatedCommand_Then private async Task Generic_GivenEntityInserted_WhenGettingEntity_ThenReturnEntity( TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE From bd5fac6f61164774884096f020fb6875d2a87b20 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Nov 2022 15:44:11 -0800 Subject: [PATCH 07/93] refactor: use ValueConverter for the ValueObjects --- .../Converters/IdConverter.cs | 15 +++++++++++ .../Converters/TimeStampConverter.cs | 15 +++++++++++ .../Converters/VersionNumberConverter.cs | 15 +++++++++++ .../Snapshots/SnapshotReferenceDbContext.cs | 20 +++++++++++++- .../Implementations/Entities/TestEntity.cs | 26 +++---------------- .../Projections/OneToOneProjection.cs | 22 ++-------------- 6 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 src/EntityDb.EntityFramework/Converters/IdConverter.cs create mode 100644 src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs create mode 100644 src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs diff --git a/src/EntityDb.EntityFramework/Converters/IdConverter.cs b/src/EntityDb.EntityFramework/Converters/IdConverter.cs new file mode 100644 index 00000000..441fcdc5 --- /dev/null +++ b/src/EntityDb.EntityFramework/Converters/IdConverter.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.ValueObjects; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using System.Linq.Expressions; + +namespace EntityDb.EntityFramework.Converters; + +internal class IdConverter : ValueConverter +{ + private static readonly Expression> IdToGuid = (id) => id.Value; + private static readonly Expression> GuidToId = (guid) => new Id(guid); + + public IdConverter() : base(IdToGuid, GuidToId, null) + { + } +} diff --git a/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs b/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs new file mode 100644 index 00000000..e5664e25 --- /dev/null +++ b/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.ValueObjects; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using System.Linq.Expressions; + +namespace EntityDb.EntityFramework.Converters; + +internal class TimeStampConverter : ValueConverter +{ + private static readonly Expression> TimeStampToDateTime = (timeStamp) => timeStamp.Value; + private static readonly Expression> DateTimeToTimeStamp = (dateTime) => new TimeStamp(dateTime); + + public TimeStampConverter() : base(TimeStampToDateTime, DateTimeToTimeStamp, null) + { + } +} diff --git a/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs b/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs new file mode 100644 index 00000000..84ef7145 --- /dev/null +++ b/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.ValueObjects; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using System.Linq.Expressions; + +namespace EntityDb.EntityFramework.Converters; + +internal class VersionNumberConverter : ValueConverter +{ + private static readonly Expression> VersionNumberToUlong = (versionNumber) => versionNumber.Value; + private static readonly Expression> UlongToVersionNumber = (@ulong) => new VersionNumber(@ulong); + + public VersionNumberConverter() : base(VersionNumberToUlong, UlongToVersionNumber, null) + { + } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs index 61e4f042..9921092c 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs @@ -1,4 +1,6 @@ -using Microsoft.EntityFrameworkCore; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.EntityFramework.Converters; +using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Snapshots; @@ -19,6 +21,22 @@ protected SnapshotReferenceDbContext(DbContextOptions dbContextOptions) : base(d { } + /// + protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) + { + configurationBuilder + .Properties() + .HaveConversion(); + + configurationBuilder + .Properties() + .HaveConversion(); + + configurationBuilder + .Properties() + .HaveConversion(); + } + /// protected override void OnModelCreating(ModelBuilder builder) { diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 2c13e24f..02961bf2 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,8 +1,6 @@ -using System.ComponentModel.DataAnnotations.Schema; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Commands; -using EntityDb.Common.Tests.Implementations.Projections; using EntityDb.Common.Tests.Implementations.Snapshots; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -14,20 +12,6 @@ public record TestEntity : IEntity, ISnapshotWithTestLogic Id.Value; - init => Id = new Id(value); - } - - [Column("VersionNumber")] - public ulong VersionNumberValue - { - get => VersionNumber.Value; - init => VersionNumber = new VersionNumber(value); - } - public static TestEntity Construct(Id entityId) { return new TestEntity @@ -38,15 +22,11 @@ public static TestEntity Construct(Id entityId) public static void Configure(OwnedNavigationBuilder, TestEntity> testEntityBuilder) { - testEntityBuilder - .Ignore(testEntity => testEntity.Id) - .Ignore(testEntity => testEntity.VersionNumber); - testEntityBuilder .HasKey(testEntity => new { - testEntity.IdValue, - testEntity.VersionNumberValue, + testEntity.Id, + testEntity.VersionNumber, }); } diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index e6eb6997..8fe81284 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -18,20 +18,6 @@ public record OneToOneProjection : IProjection, ISnapshotWit public required Id Id { get; init; } public VersionNumber VersionNumber { get; init; } - [Column("Id")] - public Guid IdValue - { - get => Id.Value; - init => Id = new Id(value); - } - - [Column("VersionNumber")] - public ulong VersionNumberValue - { - get => VersionNumber.Value; - init => VersionNumber = new VersionNumber(value); - } - public static OneToOneProjection Construct(Id projectionId) { return new OneToOneProjection @@ -42,15 +28,11 @@ public static OneToOneProjection Construct(Id projectionId) public static void Configure(OwnedNavigationBuilder, OneToOneProjection> oneToOneProjectionBuilder) { - oneToOneProjectionBuilder - .Ignore(oneToOneProjection => oneToOneProjection.Id) - .Ignore(oneToOneProjection => oneToOneProjection.VersionNumber); - oneToOneProjectionBuilder .HasKey(oneToOneProjection => new { - oneToOneProjection.IdValue, - oneToOneProjection.VersionNumberValue, + oneToOneProjection.Id, + oneToOneProjection.VersionNumber, }); } From 2e45753d3f1623f572fb24461d0fed26cbb65d5f Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Nov 2022 15:57:31 -0800 Subject: [PATCH 08/93] refactor: make choosing table names mandatory --- .../Snapshots/SnapshotReferenceDbContext.cs | 7 ++++++- .../SnapshotReferenceTypeConfiguration.cs | 17 +++++++++++++++++ .../DbContexts/GenericDbContext.cs | 14 ++------------ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs index 9921092c..53ec24af 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs @@ -11,6 +11,11 @@ namespace EntityDb.EntityFramework.Snapshots; public abstract class SnapshotReferenceDbContext : DbContext where TSnapshot : class { + /// + /// The TypeConfiguration for Snapshot References. + /// + protected abstract SnapshotReferenceTypeConfiguration SnapshotReferenceTypeConfiguration { get; } + /// /// A database set for resolving snapshots from snapshot pointers. /// @@ -43,6 +48,6 @@ protected override void OnModelCreating(ModelBuilder builder) base.OnModelCreating(builder); builder - .ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); + .ApplyConfiguration(SnapshotReferenceTypeConfiguration); } } diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs index e6696a69..35fa7b66 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -7,9 +7,25 @@ namespace EntityDb.EntityFramework.Snapshots; public class SnapshotReferenceTypeConfiguration : IEntityTypeConfiguration> where TSnapshot : class { + private readonly string _snapshotReferencesTableName; + private readonly string _snapshotsTableName; + + /// + /// Configure the napshot Reference Type. + /// + /// The name of the table for snapshot references. + /// The name of the table for snapshots. + public SnapshotReferenceTypeConfiguration(string snapshotReferencesTableName, string snapshotsTableName) + { + _snapshotReferencesTableName = snapshotReferencesTableName; + _snapshotsTableName = snapshotsTableName; + } + /// public virtual void Configure(EntityTypeBuilder> snapshotReferenceBuilder) { + snapshotReferenceBuilder.ToTable(_snapshotReferencesTableName); + snapshotReferenceBuilder .HasKey ( @@ -35,5 +51,6 @@ public virtual void Configure(EntityTypeBuilder> sn /// The builder to be used to configure the snapshot type. protected virtual void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder) { + snapshotBuilder.ToTable(_snapshotsTableName); } } diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 6a182b3b..4076b3f7 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -24,30 +24,20 @@ public GenericDbContext(DbContextOptions> options) : } } - protected override void OnModelCreating(ModelBuilder builder) - { - builder - .ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); - } + protected override SnapshotReferenceTypeConfiguration SnapshotReferenceTypeConfiguration { get; } = new(); } - public class SnapshotReferenceTypeConfiguration : EntityFramework.Snapshots.SnapshotReferenceTypeConfiguration where TSnapshot : class, ISnapshotWithTestLogic { - public override void Configure(EntityTypeBuilder> snapshotReferenceBuilder) + public SnapshotReferenceTypeConfiguration() : base($"{typeof(TSnapshot).Name}SnapshotReferences", $"{typeof(TSnapshot).Name}Snapshots") { - base.Configure(snapshotReferenceBuilder); - - snapshotReferenceBuilder.ToTable($"{typeof(TSnapshot).Name}SnapshotReferences"); } protected override void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder) { base.Configure(snapshotBuilder); - snapshotBuilder.ToTable($"{typeof(TSnapshot).Name}Snapshots"); - TSnapshot.Configure(snapshotBuilder); } } \ No newline at end of file From f33deb380527405f8ee42ba4a5ebee192808f362 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Nov 2022 16:05:00 -0800 Subject: [PATCH 09/93] refactor: use ValueObjects instead of Raw Values to keep usage consistent --- .../Sessions/EntityFrameworkSession.cs | 8 ++++---- .../Snapshots/SnapshotReference.cs | 8 +++++--- .../SnapshotReferenceTypeConfiguration.cs | 15 ++++++--------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 7c52ffa1..efe6d0d1 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -25,8 +25,8 @@ public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessi private static Expression, bool>> SnapshotPointerPredicate(Pointer snapshotPointer) { return snapshotReference => - snapshotReference.PointerId == snapshotPointer.Id.Value && - snapshotReference.PointerVersionNumber == snapshotPointer.VersionNumber.Value; + snapshotReference.PointerId == snapshotPointer.Id && + snapshotReference.PointerVersionNumber == snapshotPointer.VersionNumber; } public async Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken) @@ -55,8 +55,8 @@ public async Task Insert(Pointer snapshotPointer, TSnapshot snapshot, Cancellati var reference = new SnapshotReference { Id = Guid.NewGuid(), - PointerId = snapshotPointer.Id.Value, - PointerVersionNumber = snapshotPointer.VersionNumber.Value, + PointerId = snapshotPointer.Id, + PointerVersionNumber = snapshotPointer.VersionNumber, Snapshot = snapshot }; diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs index 572b6dc0..2728dd8e 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs @@ -1,4 +1,6 @@ -namespace EntityDb.EntityFramework.Snapshots; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.EntityFramework.Snapshots; /// /// Represents a unique snapshot and its pointer. @@ -14,12 +16,12 @@ public class SnapshotReference /// /// The ID of the Snapshot Pointer. /// - public required Guid PointerId { get; init; } + public required Id PointerId { get; init; } /// /// The Version Number of the Snapshot Pointer. /// - public required ulong PointerVersionNumber { get; init; } + public required VersionNumber PointerVersionNumber { get; init; } /// /// The Snapshot. diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs index 35fa7b66..56c4443d 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -27,17 +27,14 @@ public virtual void Configure(EntityTypeBuilder> sn snapshotReferenceBuilder.ToTable(_snapshotReferencesTableName); snapshotReferenceBuilder - .HasKey - ( - nameof(SnapshotReference.Id) - ); + .HasKey(snapshotReference => snapshotReference.Id); snapshotReferenceBuilder - .HasAlternateKey - ( - nameof(SnapshotReference.PointerId), - nameof(SnapshotReference.PointerVersionNumber) - ); + .HasAlternateKey(snapshotReference => new + { + snapshotReference.PointerId, + snapshotReference.PointerVersionNumber + }); var snapshotBuilder = snapshotReferenceBuilder .OwnsOne(snapshotReference => snapshotReference.Snapshot); From f4512fe7bdb03029a4ea5eb55f278f5e40c6f02c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Nov 2022 18:54:20 -0800 Subject: [PATCH 10/93] refactor: don't require a named DbSet property prevents using one DbContext for multiple snapshots (and therefore prevents related snapshots) --- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../Sessions/EntityFrameworkSession.cs | 8 +++--- ...ntityFrameworkSnapshotRepositoryFactory.cs | 2 +- .../Snapshots/SnapshotReferenceDbContext.cs | 27 +++---------------- .../DbContexts/GenericDbContext.cs | 9 +++++-- 5 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index 531fd96f..4353deae 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -24,7 +24,7 @@ public static class ServiceCollectionExtensions /// Modifies the behavior of the repository to accomodate tests. public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) where TSnapshot : class - where TDbContext : SnapshotReferenceDbContext + where TDbContext : SnapshotReferenceDbContext { serviceCollection.Add> ( diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index efe6d0d1..eb0a176c 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -11,7 +11,7 @@ namespace EntityDb.EntityFramework.Sessions; internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession where TSnapshot : class - where TDbContext : SnapshotReferenceDbContext + where TDbContext : SnapshotReferenceDbContext { private readonly TDbContext _dbContext; private readonly EntityFrameworkSnapshotSessionOptions _options; @@ -33,14 +33,14 @@ public async Task Delete(IEnumerable snapshotPointers, CancellationToke { AssertNotReadOnly(); - await _dbContext.SnapshotReferences + await _dbContext.Set>() .Where(PredicateExpressionBuilder.Or(snapshotPointers, SnapshotPointerPredicate)) .ExecuteDeleteAsync(cancellationToken); } public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) { - var reference = await _dbContext.SnapshotReferences + var reference = await _dbContext.Set>() .Where(SnapshotPointerPredicate(snapshotPointer)) .AsNoTracking() .SingleOrDefaultAsync(cancellationToken); @@ -60,7 +60,7 @@ public async Task Insert(Pointer snapshotPointer, TSnapshot snapshot, Cancellati Snapshot = snapshot }; - _dbContext.SnapshotReferences.Add(reference); + _dbContext.Set>().Add(reference); await _dbContext.SaveChangesAsync(cancellationToken); } diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index fc6ba80e..9e22c2ff 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -11,7 +11,7 @@ namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, ISnapshotRepositoryFactory where TSnapshot : class - where TDbContext : SnapshotReferenceDbContext + where TDbContext : SnapshotReferenceDbContext { private readonly IServiceProvider _serviceProvider; private readonly IDbContextFactory _dbContextFactory; diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs index 53ec24af..2f8f5864 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs @@ -5,24 +5,12 @@ namespace EntityDb.EntityFramework.Snapshots; /// -/// A minimal DbContext for snapshot references. +/// A DbContext that adds basic converters for types defined in /// -/// The type of the snapshot -public abstract class SnapshotReferenceDbContext : DbContext - where TSnapshot : class +public class SnapshotReferenceDbContext : DbContext { - /// - /// The TypeConfiguration for Snapshot References. - /// - protected abstract SnapshotReferenceTypeConfiguration SnapshotReferenceTypeConfiguration { get; } - - /// - /// A database set for resolving snapshots from snapshot pointers. - /// - public required DbSet> SnapshotReferences { get; set; } - /// - protected SnapshotReferenceDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) + public SnapshotReferenceDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) { } @@ -41,13 +29,4 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura .Properties() .HaveConversion(); } - - /// - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - - builder - .ApplyConfiguration(SnapshotReferenceTypeConfiguration); - } } diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 4076b3f7..ee55e0af 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -7,7 +7,7 @@ namespace EntityDb.Common.Tests.Implementations.DbContexts; -internal class GenericDbContext : SnapshotReferenceDbContext +internal class GenericDbContext : SnapshotReferenceDbContext where TSnapshot : class, ISnapshotWithTestLogic { public GenericDbContext(DbContextOptions> options) : base(options) @@ -24,7 +24,12 @@ public GenericDbContext(DbContextOptions> options) : } } - protected override SnapshotReferenceTypeConfiguration SnapshotReferenceTypeConfiguration { get; } = new(); + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); + } } public class SnapshotReferenceTypeConfiguration : EntityFramework.Snapshots.SnapshotReferenceTypeConfiguration From d54256f2079114b25a04b51e92954bb63080e5e6 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Nov 2022 20:23:12 -0800 Subject: [PATCH 11/93] refactor: use real transactions for entity framework snapshots in test mode --- .../Extensions/ServiceCollectionExtensions.cs | 2 +- ...bTransactionRepositoryFactoryExtensions.cs | 18 ++++++ .../Sessions/EntityFrameworkSession.cs | 35 +++++++++++ .../Sessions/IEntityFrameworkSession.cs | 11 +++- .../TestModeEntityFrameworkSession.cs | 45 +++++++++++++ .../EntityFrameworkSnapshotRepository.cs | 34 ++++++++-- ...ntityFrameworkSnapshotRepositoryFactory.cs | 17 +++-- ...ntityFrameworkSnapshotRepositoryFactory.cs | 24 +++++++ ...ntityFrameworkSnapshotRepositoryFactory.cs | 63 +++++++++++++++++++ test/EntityDb.Common.Tests/TestsBase.cs | 14 +++-- 10 files changed, 242 insertions(+), 21 deletions(-) create mode 100644 src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs create mode 100644 src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs create mode 100644 src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index 4353deae..5597292a 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -31,7 +31,7 @@ public static void AddEntityFrameworkSnapshots(this IServ testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient ); - serviceCollection.Add + serviceCollection.Add> ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, serviceProvider => serviceProvider diff --git a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs new file mode 100644 index 00000000..a65d1aea --- /dev/null +++ b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs @@ -0,0 +1,18 @@ +using EntityDb.EntityFramework.Snapshots; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.EntityFramework.Extensions; + +internal static class EntityFrameworkSnapshotRepositoryFactoryExtensions +{ + + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static IEntityFrameworkSnapshotRepositoryFactory UseTestMode( + this IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoyFactory, + bool testMode) + { + return testMode + ? new TestModeEntityFrameworkSnapshotRepositoryFactory(entityFrameworkSnapshotRepositoyFactory) + : entityFrameworkSnapshotRepositoyFactory; + } +} diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index eb0a176c..7466d640 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -3,6 +3,7 @@ using EntityDb.EntityFramework.Predicates; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using System.Linq.Expressions; using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; @@ -16,6 +17,8 @@ internal class EntityFrameworkSession : DisposableResourc private readonly TDbContext _dbContext; private readonly EntityFrameworkSnapshotSessionOptions _options; + private IDbContextTransaction? Transaction { get; set; } + public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessionOptions options) { _dbContext = dbContext; @@ -82,4 +85,36 @@ EntityFrameworkSnapshotSessionOptions options { return ActivatorUtilities.CreateInstance>(serviceProvider, dbContext, options); } + + public async Task StartTransaction(CancellationToken cancellationToken) + { + Transaction = await _dbContext.Database.BeginTransactionAsync(cancellationToken); + } + + public async Task CommitTransaction(CancellationToken cancellationToken) + { + if (Transaction != null) + { + await Transaction.CommitAsync(cancellationToken); + await Transaction.DisposeAsync(); + + Transaction = null; + } + } + + public async Task AbortTransaction(CancellationToken cancellationToken) + { + if (Transaction != null) + { + await Transaction.RollbackAsync(cancellationToken); + await Transaction.DisposeAsync(); + + Transaction = null; + } + } + + public IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) + { + return new EntityFrameworkSession(_dbContext, snapshotSessionOptions); + } } diff --git a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs index 62dca2ee..c1a89863 100644 --- a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs @@ -1,10 +1,17 @@ +using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.EntityFramework.Sessions; -internal interface IEntityFrameworkSession +internal interface IEntityFrameworkSession : IDisposableResource { - Task Insert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken); + IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); + + Task StartTransaction(CancellationToken cancellationToken); + Task CommitTransaction(CancellationToken cancellationToken); + Task AbortTransaction(CancellationToken cancellationToken); + + Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken); Task Get(Pointer snapshotPointer, CancellationToken cancellationToken); diff --git a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs new file mode 100644 index 00000000..de0d1ba9 --- /dev/null +++ b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs @@ -0,0 +1,45 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.EntityFramework.Sessions; + +internal record TestModeEntityFrameworkSession(IEntityFrameworkSession EntityFrameworkSession) : DisposableResourceBaseRecord, IEntityFrameworkSession +{ + public Task StartTransaction(CancellationToken cancellationToken) + { + // Test Mode Transactions are started in the Test Mode Repository Factory + return Task.CompletedTask; + } + + public Task CommitTransaction(CancellationToken cancellationToken) + { + // Test Mode Transactions are never committed + return Task.CompletedTask; + } + + public Task AbortTransaction(CancellationToken cancellationToken) + { + // Test Mode Transactions are aborted in the Test Mode Repository Factory + return Task.CompletedTask; + } + + public Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) + { + return EntityFrameworkSession.Upsert(snapshotPointer, snapshot, cancellationToken); + } + + public Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) + { + return EntityFrameworkSession.Get(snapshotPointer, cancellationToken); + } + + public Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken) + { + return EntityFrameworkSession.Delete(snapshotPointers, cancellationToken); + } + + public IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) + { + return this with { EntityFrameworkSession = EntityFrameworkSession.WithSnapshotSessionOptions(snapshotSessionOptions) }; + } +} diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs index 5c2e3914..94e7db08 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs @@ -16,9 +16,22 @@ public EntityFrameworkSnapshotRepository(IEntityFrameworkSession enti public async Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) { - await _entityFrameworkSession.Delete(snapshotPointers, cancellationToken); + try + { + await _entityFrameworkSession.StartTransaction(cancellationToken); - return true; + await _entityFrameworkSession.Delete(snapshotPointers, cancellationToken); + + await _entityFrameworkSession.CommitTransaction(cancellationToken); + + return true; + } + catch + { + await _entityFrameworkSession.AbortTransaction(cancellationToken); + + throw; + } } public Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default) @@ -28,8 +41,21 @@ public async Task DeleteSnapshots(Pointer[] snapshotPointers, Cancellation public async Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken = default) { - await _entityFrameworkSession.Insert(snapshotPointer, snapshot, cancellationToken); + try + { + await _entityFrameworkSession.StartTransaction(cancellationToken); + + await _entityFrameworkSession.Upsert(snapshotPointer, snapshot, cancellationToken); + + await _entityFrameworkSession.CommitTransaction(cancellationToken); + + return true; + } + catch + { + await _entityFrameworkSession.AbortTransaction(cancellationToken); - return true; + throw; + } } } diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index 9e22c2ff..d48b9f18 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -9,7 +9,7 @@ namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, - ISnapshotRepositoryFactory + IEntityFrameworkSnapshotRepositoryFactory where TSnapshot : class where TDbContext : SnapshotReferenceDbContext { @@ -29,13 +29,8 @@ IOptionsFactory optionsFactory _optionsFactory = optionsFactory; } - public async Task> CreateRepository(string snapshotSessionOptionsName, - CancellationToken cancellationToken = default) + public ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession) { - var options = _optionsFactory.Create(snapshotSessionOptionsName); - - var entityFrameworkSession = await CreateSession(options, cancellationToken); - var entityFrameworkSnapshotRepository = new EntityFrameworkSnapshotRepository ( entityFrameworkSession @@ -44,8 +39,7 @@ public async Task> CreateRepository(string snapsh return TryCatchSnapshotRepository.Create(_serviceProvider, entityFrameworkSnapshotRepository); } - private async Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, - CancellationToken cancellationToken) + public async Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken) { var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); @@ -62,4 +56,9 @@ public static EntityFrameworkSnapshotRepositoryFactory Cr keyNamespace ); } + + public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) + { + return _optionsFactory.Create(snapshotSessionOptionsName); + } } diff --git a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs new file mode 100644 index 00000000..6cde2cc5 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.EntityFramework.Sessions; + +namespace EntityDb.EntityFramework.Snapshots; + +internal interface IEntityFrameworkSnapshotRepositoryFactory : ISnapshotRepositoryFactory +{ + async Task> ISnapshotRepositoryFactory.CreateRepository( + string snapshotSessionOptionsName, CancellationToken cancellationToken) + { + var options = GetTransactionSessionOptions(snapshotSessionOptionsName); + + var entityFrameworkSession = await CreateSession(options, cancellationToken); + + return CreateRepository(entityFrameworkSession); + } + + EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName); + + Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, + CancellationToken cancellationToken); + + ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession); +} diff --git a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs new file mode 100644 index 00000000..e6deb576 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs @@ -0,0 +1,63 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Common.Disposables; +using EntityDb.EntityFramework.Sessions; + +namespace EntityDb.EntityFramework.Snapshots; + +internal class TestModeEntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory +{ + private readonly IEntityFrameworkSnapshotRepositoryFactory _entityFrameworkSnapshotRepositoryFactory; + + private (IEntityFrameworkSession Normal, TestModeEntityFrameworkSession TestMode)? _sessions; + + public TestModeEntityFrameworkSnapshotRepositoryFactory( + IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory) + { + _entityFrameworkSnapshotRepositoryFactory = entityFrameworkSnapshotRepositoryFactory; + } + + public ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession) + { + return _entityFrameworkSnapshotRepositoryFactory.CreateRepository(entityFrameworkSession); + } + + public async Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, + CancellationToken cancellationToken) + { + if (_sessions.HasValue) + { + return _sessions.Value.TestMode + .WithSnapshotSessionOptions(options); + } + + var normalOptions = new EntityFrameworkSnapshotSessionOptions + { + ReadOnly = false + }; + + var normalSession = await _entityFrameworkSnapshotRepositoryFactory.CreateSession(normalOptions, cancellationToken); + + var testModeSession = new TestModeEntityFrameworkSession(normalSession); + + await normalSession.StartTransaction(default); + + _sessions = (normalSession, testModeSession); + + return _sessions.Value.TestMode + .WithSnapshotSessionOptions(options); + } + + public override async ValueTask DisposeAsync() + { + if (_sessions.HasValue) + { + await _sessions.Value.Normal.AbortTransaction(default); + await _sessions.Value.Normal.DisposeAsync(); + } + } + + public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) + { + return _entityFrameworkSnapshotRepositoryFactory.GetTransactionSessionOptions(snapshotSessionOptionsName); + } +} diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 07f074dc..6759b9d9 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -399,17 +399,21 @@ protected static (ILoggerFactory Logger, Action LoggerVerifier) GetMocked It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), + It.IsAny(), It.IsAny>() )); + loggerMock + .Setup(logger => logger.IsEnabled(It.IsAny())) + .Returns((LogLevel logLevel) => logLevel == LogLevel.Error); + loggerMock .Setup(logger => logger.Log ( - LogLevel.Error, + It.Is(logLevel => logLevel == LogLevel.Error), It.IsAny(), It.IsAny(), - It.IsAny(), + It.Is(exception => exception is TException), It.IsAny>() )) .Verifiable(); @@ -430,10 +434,10 @@ void Verifier(Times times) ( logger => logger.Log ( - LogLevel.Error, + It.Is(logLevel => logLevel == LogLevel.Error), It.IsAny(), It.IsAny(), - It.IsAny(), + It.Is(exception => exception is TException), It.IsAny>() ), times From 030514e0a707c2e31506a068d2a3351b528fadcc Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Nov 2022 20:23:38 -0800 Subject: [PATCH 12/93] refactor: update snapshot reference if one already exists --- .../Sessions/EntityFrameworkSession.cs | 31 +++++++++++++------ .../Snapshots/SnapshotReference.cs | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 7466d640..08ca1d6a 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -51,19 +51,32 @@ await _dbContext.Set>() return reference?.Snapshot; } - public async Task Insert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) + public async Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) { AssertNotReadOnly(); - var reference = new SnapshotReference - { - Id = Guid.NewGuid(), - PointerId = snapshotPointer.Id, - PointerVersionNumber = snapshotPointer.VersionNumber, - Snapshot = snapshot - }; + var dbSet = _dbContext.Set>(); + + var previousReference = await dbSet + .Where(SnapshotPointerPredicate(snapshotPointer)) + .SingleOrDefaultAsync(cancellationToken); - _dbContext.Set>().Add(reference); + if (previousReference != null) + { + previousReference.Snapshot = snapshot; + } + else + { + var reference = new SnapshotReference + { + Id = Guid.NewGuid(), + PointerId = snapshotPointer.Id, + PointerVersionNumber = snapshotPointer.VersionNumber, + Snapshot = snapshot + }; + + dbSet.Add(reference); + } await _dbContext.SaveChangesAsync(cancellationToken); } diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs index 2728dd8e..b7140d0d 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs @@ -26,5 +26,5 @@ public class SnapshotReference /// /// The Snapshot. /// - public required TSnapshot Snapshot { get; init; } + public required TSnapshot Snapshot { get; set; } } From 0d61f8ef5ec6dd2cd80a5c94ee409f3ba826ed9e Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Nov 2022 20:48:59 -0800 Subject: [PATCH 13/93] chore: coverage for TimeStamp converter --- .../Implementations/Projections/OneToOneProjection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 8fe81284..183be609 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -16,6 +16,7 @@ namespace EntityDb.Common.Tests.Implementations.Projections; public record OneToOneProjection : IProjection, ISnapshotWithTestLogic { public required Id Id { get; init; } + public TimeStamp LastEventAt { get; init; } public VersionNumber VersionNumber { get; init; } public static OneToOneProjection Construct(Id projectionId) @@ -52,6 +53,7 @@ public OneToOneProjection Reduce(IEntityAnnotation annotatedCommand) { IEntityAnnotation> reducer => reducer.Data.Reduce(this) with { + LastEventAt = reducer.TransactionTimeStamp, VersionNumber = reducer.EntityVersionNumber }, _ => throw new NotSupportedException() From b56aaf42374a1b2f488c15ca848724f1485cfc3c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Nov 2022 20:49:16 -0800 Subject: [PATCH 14/93] chore: exclude ToString overrides from coverage --- .../Sessions/EntityFrameworkSnapshotSessionOptions.cs | 2 ++ .../Sessions/MongoDbTransactionSessionOptions.cs | 2 ++ src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs | 2 ++ src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 6ca17245..1ed25e2f 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Snapshots; +using System.Diagnostics.CodeAnalysis; namespace EntityDb.EntityFramework.Sessions; @@ -13,6 +14,7 @@ public class EntityFrameworkSnapshotSessionOptions public bool ReadOnly { get; set; } /// + [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { return $"{nameof(EntityFrameworkSnapshotSessionOptions)}"; diff --git a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs index 44d7789f..aa2daaae 100644 --- a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs +++ b/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Transactions; using MongoDB.Driver; +using System.Diagnostics.CodeAnalysis; namespace EntityDb.MongoDb.Sessions; @@ -34,6 +35,7 @@ public class MongoDbTransactionSessionOptions public TimeSpan? WriteTimeout { get; set; } /// + [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { return $"{nameof(MongoDbTransactionSessionOptions)}"; diff --git a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs index cc55c78c..65c27f7b 100644 --- a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Snapshots; using StackExchange.Redis; +using System.Diagnostics.CodeAnalysis; namespace EntityDb.Redis.Sessions; @@ -30,6 +31,7 @@ public class RedisSnapshotSessionOptions public bool SecondaryPreferred { get; set; } /// + [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { return $"{nameof(RedisSnapshotSessionOptions)}<{typeof(TSnapshot).Name}"; diff --git a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs b/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs index dd4cdd39..41ae493c 100644 --- a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs +++ b/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Transactions; using System.Data; +using System.Diagnostics.CodeAnalysis; namespace EntityDb.SqlDb.Sessions; @@ -39,6 +40,7 @@ public class SqlDbTransactionSessionOptions public TimeSpan? ReadTimeout { get; set; } /// + [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { return $"{nameof(SqlDbTransactionSessionOptions)}"; From 2ed12245313267d6c6133659a0511329e23a23b2 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Nov 2022 20:49:50 -0800 Subject: [PATCH 15/93] chore: exclude functioning CommitTransaction from tests Tests should be running in TestMode, and should not actually commit data. --- .../Sessions/EntityFrameworkSession.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 08ca1d6a..2c952e66 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; @@ -104,6 +105,8 @@ public async Task StartTransaction(CancellationToken cancellationToken) Transaction = await _dbContext.Database.BeginTransactionAsync(cancellationToken); } + [ExcludeFromCodeCoverage(Justification = + "Tests should run with the Debug configuration, and should not execute this method.")] public async Task CommitTransaction(CancellationToken cancellationToken) { if (Transaction != null) From f3f68915b23dd5d4bdb2a68dfb27239ff8e5d7cd Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Nov 2022 20:52:22 -0800 Subject: [PATCH 16/93] chore: remove unused method --- .../EntityFrameworkSnapshotRepositoryFactory.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index d48b9f18..a208be47 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -46,17 +46,6 @@ public async Task> CreateSession(EntityFramew return EntityFrameworkSession.Create(_serviceProvider, dbContext, options); } - public static EntityFrameworkSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, - string connectionString, string keyNamespace) - { - return ActivatorUtilities.CreateInstance> - ( - serviceProvider, - connectionString, - keyNamespace - ); - } - public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) { return _optionsFactory.Create(snapshotSessionOptionsName); From 0292dabbd05a3175b06f0f51a019e294f7728632 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Nov 2022 21:55:39 -0800 Subject: [PATCH 17/93] fix: swap order of methods --- src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 2c952e66..1f29b861 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -45,8 +45,8 @@ await _dbContext.Set>() public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) { var reference = await _dbContext.Set>() - .Where(SnapshotPointerPredicate(snapshotPointer)) .AsNoTracking() + .Where(SnapshotPointerPredicate(snapshotPointer)) .SingleOrDefaultAsync(cancellationToken); return reference?.Snapshot; From e69b30b9be5aef9bf35ceba6e727e0869a01f6ec Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Nov 2022 21:57:51 -0800 Subject: [PATCH 18/93] fix: working delete method --- .../Sessions/EntityFrameworkSession.cs | 10 +++- .../Snapshots/SnapshotTests.cs | 53 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 1f29b861..e6cc839b 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -37,9 +37,15 @@ public async Task Delete(IEnumerable snapshotPointers, CancellationToke { AssertNotReadOnly(); - await _dbContext.Set>() + var set = _dbContext.Set>(); + + var snapshots = await set .Where(PredicateExpressionBuilder.Or(snapshotPointers, SnapshotPointerPredicate)) - .ExecuteDeleteAsync(cancellationToken); + .ToListAsync(cancellationToken); + + set.RemoveRange(snapshots); + + await _dbContext.SaveChangesAsync(cancellationToken); } public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) diff --git a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs index f970f81e..ec1523c6 100644 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs @@ -114,6 +114,59 @@ public Task ); } + private async Task + Generic_GivenInsertedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot( + SnapshotAdder snapshotAdder) + where TSnapshot : class, ISnapshotWithTestLogic + { + // ARRANGE + + Pointer latestSnapshotPointer = Id.NewId(); + + var snapshot = TSnapshot.Construct(latestSnapshotPointer.Id).WithVersionNumber(new VersionNumber(5000)); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + snapshotAdder.AddDependencies.Invoke(serviceCollection); + + TSnapshot.ShouldRecordAsLatestLogic.Value = (_, _) => true; + }); + + await using var writeSnapshotRepository = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateRepository(TestSessionOptions.Write); + + var inserted = await writeSnapshotRepository.PutSnapshot(latestSnapshotPointer, snapshot); + + // ARRANGE ASSERTIONS + + inserted.ShouldBeTrue(); + + // ACT + + var deleted = await writeSnapshotRepository.DeleteSnapshots(new[] { latestSnapshotPointer }); + + var finalSnapshot = await writeSnapshotRepository.GetSnapshotOrDefault(latestSnapshotPointer); + + // ASSERT + + deleted.ShouldBeTrue(); + + finalSnapshot.ShouldBe(default); + } + + [Theory] + [MemberData(nameof(AddEntitySnapshots))] + [MemberData(nameof(AddProjectionSnapshots))] + public Task GivenInsertedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot(SnapshotAdder snapshotAdder) + { + return RunGenericTestAsync + ( + new[] { snapshotAdder.SnapshotType }, + new object?[] { snapshotAdder } + ); + } + private async Task Generic_GivenInsertedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot( SnapshotAdder snapshotAdder) where TSnapshot : class, ISnapshotWithTestLogic From 2e4454823d719b38dfe3d6fe246813e1e842b04c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Mon, 19 Dec 2022 16:04:52 -0800 Subject: [PATCH 19/93] refactor: remove various ITransactionStep concept.. if you want to add/delete tags/leases, must use the I(Add/Delete)(Leases/Tags)Command interface --- .../Commands/IAddLeasesCommand.cs | 15 ++ .../Commands/IAddTagsCommand.cs | 15 ++ .../Commands/IDeleteLeasesCommand.cs | 15 ++ .../Commands/IDeleteTagsCommand.cs | 15 ++ .../ISingleEntityTransactionBuilder.cs | 34 +---- .../Builders/ITransactionBuilder.cs | 38 +---- .../Transactions/ITransaction.cs | 8 +- ...nsactionStep.cs => ITransactionCommand.cs} | 48 +++--- .../Steps/IAddLeasesTransactionStep.cs | 15 -- .../Steps/IAddTagsTransactionStep.cs | 15 -- .../Steps/IAppendCommandTransactionStep.cs | 22 --- .../Steps/IDeleteLeasesTransactionStep.cs | 15 -- .../Steps/IDeleteTagsTransactionStep.cs | 15 -- .../ValueObjects/VersionNumber.cs | 9 ++ .../Commands/IAddLeasesCommand.cs | 23 --- .../Commands/IAddTagsCommand.cs | 23 --- .../Commands/IDeleteLeasesCommand.cs | 23 --- .../Commands/IDeleteTagsCommand.cs | 23 --- .../OptimisticConcurrencyException.cs | 9 +- .../VersionZeroReservedException.cs | 7 +- .../SingleEntityTransactionBuilder.cs | 28 ---- .../Builders/TransactionBuilder.cs | 138 ++---------------- .../ITransactionCommandWithEntitySnapshot.cs | 8 + .../Steps/AddLeasesTransactionStep.cs | 10 -- .../Steps/AppendCommandTransactionStep.cs | 10 -- .../Steps/DeleteLeasesTransactionStep.cs | 10 -- .../Steps/DeleteTagsTransactionStep.cs | 10 -- .../Transactions/Steps/TagTransactionStep.cs | 10 -- .../Transactions/Steps/TransactionStepBase.cs | 12 -- ...titySnapshotTransactionCommandProcessor.cs | 42 ++++++ .../EntitySnapshotTransactionProcessor.cs | 12 +- .../EntitySnapshotTransactionStepProcessor.cs | 43 ------ .../ISnapshotTransactionCommandProcessor.cs | 8 + .../Processors/ITransactionStepProcessor.cs | 9 -- ...ionSnapshotTransactionCommandProcessor.cs} | 27 ++-- .../ProjectionSnapshotTransactionProcessor.cs | 12 +- ...apshotTransactionCommandProcessorCache.cs} | 2 +- .../SnapshotTransactionProcessorBase.cs | 12 +- .../Transactions/Transaction.cs | 3 +- .../Transactions/TransactionCommand.cs | 11 ++ .../TransactionCommandWithSnapshot.cs | 6 + .../Documents/AgentSignatureDocument.cs | 2 +- .../Documents/CommandDocument.cs | 11 +- .../Documents/LeaseDocument.cs | 16 +- src/EntityDb.MongoDb/Documents/TagDocument.cs | 16 +- .../MongoDbTransactionRepository.cs | 91 +++++------- .../AgentSignature/AgentSignatureDocument.cs | 4 +- .../Documents/Command/CommandDocument.cs | 13 +- .../Documents/Lease/LeaseDocument.cs | 19 +-- .../Documents/Tag/TagDocument.cs | 17 ++- .../SqlDbTransactionRepository.cs | 92 +++++------- .../Entities/EntityTests.cs | 8 +- .../Implementations/Commands/DoNothing.cs | 37 ++++- .../Implementations/Commands/StoreNumber.cs | 19 ++- .../Seeders/TransactionCommandSeeder.cs | 29 ++++ .../Seeders/TransactionSeeder.cs | 33 +---- .../SingleEntityTransactionBuilderTests.cs | 26 ++-- .../Transactions/TransactionTests.cs | 72 ++++----- 58 files changed, 479 insertions(+), 836 deletions(-) create mode 100644 src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs create mode 100644 src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs create mode 100644 src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs create mode 100644 src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs rename src/EntityDb.Abstractions/Transactions/{Steps/ITransactionStep.cs => ITransactionCommand.cs} (57%) delete mode 100644 src/EntityDb.Abstractions/Transactions/Steps/IAddLeasesTransactionStep.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/Steps/IAddTagsTransactionStep.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/Steps/IAppendCommandTransactionStep.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/Steps/IDeleteLeasesTransactionStep.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/Steps/IDeleteTagsTransactionStep.cs delete mode 100644 src/EntityDb.Common/Commands/IAddLeasesCommand.cs delete mode 100644 src/EntityDb.Common/Commands/IAddTagsCommand.cs delete mode 100644 src/EntityDb.Common/Commands/IDeleteLeasesCommand.cs delete mode 100644 src/EntityDb.Common/Commands/IDeleteTagsCommand.cs create mode 100644 src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs delete mode 100644 src/EntityDb.Common/Transactions/Steps/AddLeasesTransactionStep.cs delete mode 100644 src/EntityDb.Common/Transactions/Steps/AppendCommandTransactionStep.cs delete mode 100644 src/EntityDb.Common/Transactions/Steps/DeleteLeasesTransactionStep.cs delete mode 100644 src/EntityDb.Common/Transactions/Steps/DeleteTagsTransactionStep.cs delete mode 100644 src/EntityDb.Common/Transactions/Steps/TagTransactionStep.cs delete mode 100644 src/EntityDb.Common/Transactions/Steps/TransactionStepBase.cs create mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionStepProcessor.cs create mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionStepProcessor.cs rename src/EntityDb.Common/Transactions/Subscribers/Processors/{ProjectionSnapshotTransactionStepProcessor.cs => ProjectionSnapshotTransactionCommandProcessor.cs} (58%) rename src/EntityDb.Common/Transactions/Subscribers/Processors/{SnapshotTransactionStepProcessorCache.cs => SnapshotTransactionCommandProcessorCache.cs} (87%) create mode 100644 src/EntityDb.Common/Transactions/TransactionCommand.cs create mode 100644 src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs diff --git a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs new file mode 100644 index 00000000..1efce3a1 --- /dev/null +++ b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Leases; + +namespace EntityDb.Abstractions.Commands; + +/// +/// Represents a command that adds leases. +/// +public interface IAddLeasesCommand +{ + /// + /// Returns the leases that need to be added. + /// + /// The leases that need to be added. + IEnumerable GetLeases(); +} diff --git a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs new file mode 100644 index 00000000..5001ecb8 --- /dev/null +++ b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Tags; + +namespace EntityDb.Abstractions.Commands; + +/// +/// Represents a command that adds tags. +/// +public interface IAddTagsCommand +{ + /// + /// Returns the tags that need to be added. + /// + /// The tags that need to be added. + IEnumerable GetTags(); +} diff --git a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs new file mode 100644 index 00000000..301cf009 --- /dev/null +++ b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Leases; + +namespace EntityDb.Abstractions.Commands; + +/// +/// Represents a command that deletes leases. +/// +public interface IDeleteLeasesCommand +{ + /// + /// Returns the leases that need to be deleted. + /// + /// The leases that need to be deleted. + IEnumerable GetLeases(); +} diff --git a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs new file mode 100644 index 00000000..bf760d07 --- /dev/null +++ b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Tags; + +namespace EntityDb.Abstractions.Commands; + +/// +/// Represents a command that deletes tags. +/// +public interface IDeleteTagsCommand +{ + /// + /// Returns the tags that need to be deleted. + /// + /// The tags that need to be deleted. + IEnumerable GetTags(); +} diff --git a/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs b/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs index 033348fa..7086df99 100644 --- a/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs +++ b/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Transactions.Builders; @@ -39,40 +37,12 @@ public interface ISingleEntityTransactionBuilder ISingleEntityTransactionBuilder Load(TEntity entity); /// - /// Adds a transaction step that appends a single command. + /// Adds a single command to the transaction. /// /// The new command that modifies the . /// The transaction builder. ISingleEntityTransactionBuilder Append(object command); - /// - /// Adds a transaction step that adds a set of s. - /// - /// The leases to be added to the . - /// The transaction builder. - ISingleEntityTransactionBuilder Add(params ILease[] leases); - - /// - /// Adds a transaction step that adds a set of s. - /// - /// The tags to be added to the . - /// The transaction builder. - ISingleEntityTransactionBuilder Add(params ITag[] tags); - - /// - /// Adds a transaction step that deletes a set of s. - /// - /// The leases to be deleted from the . - /// The transaction builder. - ISingleEntityTransactionBuilder Delete(params ILease[] leases); - - /// - /// Adds a transaction step that deletes a set of s. - /// - /// The tags to be deleted from the . - /// The transaction builder. - ISingleEntityTransactionBuilder Delete(params ITag[] tags); - /// /// Returns a new instance of . /// diff --git a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs b/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs index db6a8182..994c39a5 100644 --- a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs +++ b/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Transactions.Builders; @@ -41,45 +39,13 @@ public interface ITransactionBuilder ITransactionBuilder Load(Id entityId, TEntity entity); /// - /// Adds a transaction step that appends a single command associated with a given entity id. + /// Adds a single command to the transaction with a given entity id. /// /// The id associated with the . /// The new command that modifies the . /// The transaction builder. ITransactionBuilder Append(Id entityId, object command); - /// - /// Adds a transaction step that adds a set of s associated with a given entity id. - /// - /// The id associated with the . - /// The leases to be added to the . - /// The transaction builder. - ITransactionBuilder Add(Id entityId, params ILease[] leases); - - /// - /// Adds a transaction step that adds a set of s associated with a given entity id. - /// - /// The id associated with the . - /// The tags to be added to the . - /// The transaction builder. - ITransactionBuilder Add(Id entityId, params ITag[] tags); - - /// - /// Adds a transaction step that deletes a set of s associated with a given entity id. - /// - /// The id associated with the . - /// The leases to be deleted from the . - /// The transaction builder. - ITransactionBuilder Delete(Id entityId, params ILease[] leases); - - /// - /// Adds a transaction step that deletes a set of s associated with a given entity id. - /// - /// The id associated with the . - /// The tags to be deleted from the . - /// The transaction builder. - ITransactionBuilder Delete(Id entityId, params ITag[] tags); - /// /// Returns a new instance of . /// diff --git a/src/EntityDb.Abstractions/Transactions/ITransaction.cs b/src/EntityDb.Abstractions/Transactions/ITransaction.cs index 83a9f926..7fb08f4b 100644 --- a/src/EntityDb.Abstractions/Transactions/ITransaction.cs +++ b/src/EntityDb.Abstractions/Transactions/ITransaction.cs @@ -1,5 +1,4 @@ -using EntityDb.Abstractions.Transactions.Steps; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.ValueObjects; using System.Collections.Immutable; namespace EntityDb.Abstractions.Transactions; @@ -29,8 +28,5 @@ public interface ITransaction /// /// A series of sets of modifiers for a set of entities. /// - /// - /// must be handled in the order they are given. - /// - ImmutableArray Steps { get; } + ImmutableArray Commands { get; } } diff --git a/src/EntityDb.Abstractions/Transactions/Steps/ITransactionStep.cs b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs similarity index 57% rename from src/EntityDb.Abstractions/Transactions/Steps/ITransactionStep.cs rename to src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs index e1630845..41ec2de9 100644 --- a/src/EntityDb.Abstractions/Transactions/Steps/ITransactionStep.cs +++ b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs @@ -1,24 +1,24 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Transactions.Steps; - -/// -/// Represents a modification to an entity. -/// -public interface ITransactionStep -{ - /// - /// The id of the entity. - /// - Id EntityId { get; } - - /// - /// The state of the entity associated with this step. - /// - object Entity { get; } - - /// - /// The version number associated with this step. - /// - VersionNumber EntityVersionNumber { get; } -} +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Transactions; + +/// +/// Represents a modification to an entity. +/// +public interface ITransactionCommand +{ + /// + /// The id of the entity. + /// + Id EntityId { get; } + + /// + /// The version number associated with this command. + /// + VersionNumber EntityVersionNumber { get; } + + /// + /// The command that needs to be appended. + /// + object Command { get; } +} diff --git a/src/EntityDb.Abstractions/Transactions/Steps/IAddLeasesTransactionStep.cs b/src/EntityDb.Abstractions/Transactions/Steps/IAddLeasesTransactionStep.cs deleted file mode 100644 index 4bba6f85..00000000 --- a/src/EntityDb.Abstractions/Transactions/Steps/IAddLeasesTransactionStep.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.Leases; -using System.Collections.Immutable; - -namespace EntityDb.Abstractions.Transactions.Steps; - -/// -/// Represents a transaction step that adds leases. -/// -public interface IAddLeasesTransactionStep : ITransactionStep -{ - /// - /// The leases that need to be added. - /// - ImmutableArray Leases { get; } -} diff --git a/src/EntityDb.Abstractions/Transactions/Steps/IAddTagsTransactionStep.cs b/src/EntityDb.Abstractions/Transactions/Steps/IAddTagsTransactionStep.cs deleted file mode 100644 index d0e54267..00000000 --- a/src/EntityDb.Abstractions/Transactions/Steps/IAddTagsTransactionStep.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.Tags; -using System.Collections.Immutable; - -namespace EntityDb.Abstractions.Transactions.Steps; - -/// -/// Represents a transaction step that adds tags. -/// -public interface IAddTagsTransactionStep : ITransactionStep -{ - /// - /// The tags that need to be added. - /// - ImmutableArray Tags { get; } -} diff --git a/src/EntityDb.Abstractions/Transactions/Steps/IAppendCommandTransactionStep.cs b/src/EntityDb.Abstractions/Transactions/Steps/IAppendCommandTransactionStep.cs deleted file mode 100644 index e2c5bf8f..00000000 --- a/src/EntityDb.Abstractions/Transactions/Steps/IAppendCommandTransactionStep.cs +++ /dev/null @@ -1,22 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Transactions.Steps; - -/// -/// Represents a transaction step that appends a command. -/// -public interface IAppendCommandTransactionStep : ITransactionStep -{ - /// - /// The command that needs to be appended. - /// - object Command { get; } - - /// - /// The expected version number of the command committed before this one. - /// - /// - /// The value zero is reserved to indicate that this command is the first command. - /// - VersionNumber PreviousEntityVersionNumber { get; } -} diff --git a/src/EntityDb.Abstractions/Transactions/Steps/IDeleteLeasesTransactionStep.cs b/src/EntityDb.Abstractions/Transactions/Steps/IDeleteLeasesTransactionStep.cs deleted file mode 100644 index 17d930da..00000000 --- a/src/EntityDb.Abstractions/Transactions/Steps/IDeleteLeasesTransactionStep.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.Leases; -using System.Collections.Immutable; - -namespace EntityDb.Abstractions.Transactions.Steps; - -/// -/// Represents a transaction step that deletes leases. -/// -public interface IDeleteLeasesTransactionStep : ITransactionStep -{ - /// - /// The leases that need to be deleted. - /// - ImmutableArray Leases { get; } -} diff --git a/src/EntityDb.Abstractions/Transactions/Steps/IDeleteTagsTransactionStep.cs b/src/EntityDb.Abstractions/Transactions/Steps/IDeleteTagsTransactionStep.cs deleted file mode 100644 index 0797f1b6..00000000 --- a/src/EntityDb.Abstractions/Transactions/Steps/IDeleteTagsTransactionStep.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.Tags; -using System.Collections.Immutable; - -namespace EntityDb.Abstractions.Transactions.Steps; - -/// -/// Represents a transaction step that deletes tags. -/// -public interface IDeleteTagsTransactionStep : ITransactionStep -{ - /// - /// The tags that need to be deleted. - /// - ImmutableArray Tags { get; } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs b/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs index 6970371a..ee81fc0e 100644 --- a/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs +++ b/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs @@ -26,6 +26,15 @@ public VersionNumber Next() return new VersionNumber(Value + 1); } + /// + /// Gets the previous version number. + /// + /// The previous version number. + public VersionNumber Previous() + { + return new VersionNumber(Value - 1); + } + /// /// Converts the numeric value of this instance to its equivalent string /// representation. diff --git a/src/EntityDb.Common/Commands/IAddLeasesCommand.cs b/src/EntityDb.Common/Commands/IAddLeasesCommand.cs deleted file mode 100644 index 3064a044..00000000 --- a/src/EntityDb.Common/Commands/IAddLeasesCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Transactions.Builders; - -namespace EntityDb.Common.Commands; - -/// -/// If a transaction needs to add any instances of , and the properties of the leases -/// are contained in the command and/or entity, a direct call to -/// -/// can be avoided by implementing this interface! -/// -/// The type of the entity -internal interface IAddLeasesCommand -{ - /// - /// Returns the leases that need to be added. - /// - /// The entity before this command was applied. - /// The entity after this command was applied. - /// The leases that need to be added. - IEnumerable GetLeases(TEntity previousEntity, TEntity nextEntity); -} diff --git a/src/EntityDb.Common/Commands/IAddTagsCommand.cs b/src/EntityDb.Common/Commands/IAddTagsCommand.cs deleted file mode 100644 index 305a4b35..00000000 --- a/src/EntityDb.Common/Commands/IAddTagsCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Transactions.Builders; - -namespace EntityDb.Common.Commands; - -/// -/// If a transaction needs to add any instances of , and the properties of the tags -/// are contained in the command and/or entity, a direct call to -/// -/// can be avoided by implementing this interface! -/// -/// The type of the entity -internal interface IAddTagsCommand -{ - /// - /// Returns the tags that need to be added. - /// - /// The entity before this command was applied. - /// The entity after this command was applied. - /// The tags that need to be added. - IEnumerable GetTags(TEntity previousEntity, TEntity nextEntity); -} diff --git a/src/EntityDb.Common/Commands/IDeleteLeasesCommand.cs b/src/EntityDb.Common/Commands/IDeleteLeasesCommand.cs deleted file mode 100644 index a8931b2d..00000000 --- a/src/EntityDb.Common/Commands/IDeleteLeasesCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Transactions.Builders; - -namespace EntityDb.Common.Commands; - -/// -/// If a transaction needs to delete any instances of , and the properties of the leases -/// are contained in the command and/or entity, a direct call to -/// -/// can be avoided by implementing this interface! -/// -/// The type of the entity -internal interface IDeleteLeasesCommand -{ - /// - /// Returns the leases that need to be deleted. - /// - /// The entity before this command was applied. - /// The entity after this command was applied. - /// The leases that need to be deleted. - IEnumerable GetLeases(TEntity previousEntity, TEntity nextEntity); -} diff --git a/src/EntityDb.Common/Commands/IDeleteTagsCommand.cs b/src/EntityDb.Common/Commands/IDeleteTagsCommand.cs deleted file mode 100644 index 3fb14693..00000000 --- a/src/EntityDb.Common/Commands/IDeleteTagsCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Transactions.Builders; - -namespace EntityDb.Common.Commands; - -/// -/// If a transaction needs to delete any instances of , and the properties of the tags -/// are contained in the command and/or entity, a direct call to -/// -/// can be avoided by implementing this interface! -/// -/// The type of the entity -internal interface IDeleteTagsCommand -{ - /// - /// Returns the tags that need to be deleted. - /// - /// The entity before this command was applied. - /// The entity after this command was applied. - /// The tags that need to be deleted. - IEnumerable GetTags(TEntity previousEntity, TEntity nextEntity); -} diff --git a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs index 3f89bd68..17d99fee 100644 --- a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs +++ b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs @@ -1,15 +1,14 @@ using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using Microsoft.Extensions.Logging; namespace EntityDb.Common.Exceptions; /// -/// The exception that is logged when an actor passes a to an -/// with a -/// that is not the actual -/// previous version number. +/// The exception that is thrown when an actor passes an to +/// with any +/// where the value of +/// is not equal to of the committed previous version number. /// /// /// A program will not be able to catch this exception if it is thrown. diff --git a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs b/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs index fffc3a52..527128ad 100644 --- a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs +++ b/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs @@ -1,14 +1,13 @@ using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Exceptions; /// /// The exception that is thrown when an actor passes an to -/// with on a -/// that implements -/// and the value of is equal to 0. +/// with any +/// where the value of +/// is equal to 0. /// /// /// Version Zero is reserved for an entity that has not yet been created/persisted. diff --git a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs index 2732246a..dada320c 100644 --- a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs +++ b/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs @@ -44,34 +44,6 @@ public ISingleEntityTransactionBuilder Append(object command) return this; } - public ISingleEntityTransactionBuilder Add(params ILease[] leases) - { - _transactionBuilder.Add(EntityId, leases); - - return this; - } - - public ISingleEntityTransactionBuilder Add(params ITag[] tags) - { - _transactionBuilder.Add(EntityId, tags); - - return this; - } - - public ISingleEntityTransactionBuilder Delete(params ILease[] leases) - { - _transactionBuilder.Delete(EntityId, leases); - - return this; - } - - public ISingleEntityTransactionBuilder Delete(params ITag[] tags) - { - _transactionBuilder.Delete(EntityId, tags); - - return this; - } - public ITransaction Build(Id transactionId) { return _transactionBuilder.Build(transactionId); diff --git a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs index f8be43df..f1fe7db6 100644 --- a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs +++ b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs @@ -3,12 +3,10 @@ using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Commands; +using EntityDb.Abstractions.Commands; using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; -using EntityDb.Common.Transactions.Steps; using System.Collections.Immutable; namespace EntityDb.Common.Transactions.Builders; @@ -18,7 +16,7 @@ internal sealed class TransactionBuilder : ITransactionBuilder { private readonly IAgent _agent; private readonly Dictionary _knownEntities = new(); - private readonly List _transactionSteps = new(); + private readonly List _transactionCommands = new(); public TransactionBuilder(IAgent agent) { @@ -51,78 +49,18 @@ public ITransactionBuilder Append(Id entityId, object command) { ConstructIfNotKnown(entityId); - var previousEntity = _knownEntities[entityId]; - var previousEntityVersionNumber = previousEntity.GetVersionNumber(); - - var nextEntity = previousEntity.Reduce(command); - var nextEntityVersionNumber = nextEntity.GetVersionNumber(); + var entity = _knownEntities[entityId].Reduce(command); + var entityVersionNumber = entity.GetVersionNumber(); - _transactionSteps.Add(new AppendCommandTransactionStep + _transactionCommands.Add(new TransactionCommandWithSnapshot { EntityId = entityId, - Entity = nextEntity, - EntityVersionNumber = nextEntityVersionNumber, + Snapshot = entity, + EntityVersionNumber = entityVersionNumber, Command = command, - PreviousEntityVersionNumber = previousEntityVersionNumber }); - _knownEntities[entityId] = nextEntity; - - if (command is IAddLeasesCommand addLeasesCommand) - { - Add(entityId, addLeasesCommand.GetLeases(previousEntity, nextEntity).ToImmutableArray()); - } - - if (command is IAddTagsCommand addTagsCommand) - { - Add(entityId, addTagsCommand.GetTags(previousEntity, nextEntity).ToImmutableArray()); - } - - if (command is IDeleteLeasesCommand deleteLeasesCommand) - { - Delete(entityId, deleteLeasesCommand.GetLeases(previousEntity, nextEntity).ToImmutableArray()); - } - - if (command is IDeleteTagsCommand deleteTagsCommand) - { - Delete(entityId, deleteTagsCommand.GetTags(previousEntity, nextEntity).ToImmutableArray()); - } - - return this; - } - - public ITransactionBuilder Add(Id entityId, params ILease[] leases) - { - ConstructIfNotKnown(entityId); - - Add(entityId, leases.ToImmutableArray()); - - return this; - } - - public ITransactionBuilder Add(Id entityId, params ITag[] tags) - { - ConstructIfNotKnown(entityId); - - Add(entityId, tags.ToImmutableArray()); - - return this; - } - - public ITransactionBuilder Delete(Id entityId, params ILease[] leases) - { - ConstructIfNotKnown(entityId); - - Delete(entityId, leases.ToImmutableArray()); - - return this; - } - - public ITransactionBuilder Delete(Id entityId, params ITag[] tags) - { - ConstructIfNotKnown(entityId); - - Delete(entityId, tags.ToImmutableArray()); + _knownEntities[entityId] = entity; return this; } @@ -134,10 +72,10 @@ public ITransaction Build(Id transactionId) Id = transactionId, TimeStamp = _agent.TimeStamp, AgentSignature = _agent.Signature, - Steps = _transactionSteps.ToImmutableArray() + Commands = _transactionCommands.ToImmutableArray() }; - _transactionSteps.Clear(); + _transactionCommands.Clear(); return transaction; } @@ -153,60 +91,4 @@ private void ConstructIfNotKnown(Id entityId) _knownEntities.Add(entityId, entity); } - - private void Add(Id entityId, ImmutableArray leases) - { - var entity = _knownEntities[entityId]; - var entityVersionNumber = entity.GetVersionNumber(); - - _transactionSteps.Add(new AddLeasesTransactionStep - { - EntityId = entityId, - Entity = entity, - EntityVersionNumber = entityVersionNumber, - Leases = leases - }); - } - - private void Add(Id entityId, ImmutableArray tags) - { - var entity = _knownEntities[entityId]; - var entityVersionNumber = entity.GetVersionNumber(); - - _transactionSteps.Add(new AddTagsTransactionStep - { - EntityId = entityId, - Entity = entity, - EntityVersionNumber = entityVersionNumber, - Tags = tags - }); - } - - private void Delete(Id entityId, ImmutableArray leases) - { - var entity = _knownEntities[entityId]; - var entityVersionNumber = entity.GetVersionNumber(); - - _transactionSteps.Add(new DeleteLeasesTransactionStep - { - EntityId = entityId, - Entity = entity, - EntityVersionNumber = entityVersionNumber, - Leases = leases - }); - } - - private void Delete(Id entityId, ImmutableArray tags) - { - var entity = _knownEntities[entityId]; - var entityVersionNumber = entity.GetVersionNumber(); - - _transactionSteps.Add(new DeleteTagsTransactionStep - { - EntityId = entityId, - Entity = entity, - EntityVersionNumber = entityVersionNumber, - Tags = tags - }); - } } diff --git a/src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs b/src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs new file mode 100644 index 00000000..4131816a --- /dev/null +++ b/src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs @@ -0,0 +1,8 @@ +using EntityDb.Abstractions.Transactions; + +namespace EntityDb.Common.Transactions; + +internal interface ITransactionCommandWithSnapshot : ITransactionCommand +{ + object Snapshot { get; } +} diff --git a/src/EntityDb.Common/Transactions/Steps/AddLeasesTransactionStep.cs b/src/EntityDb.Common/Transactions/Steps/AddLeasesTransactionStep.cs deleted file mode 100644 index a90a0595..00000000 --- a/src/EntityDb.Common/Transactions/Steps/AddLeasesTransactionStep.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Transactions.Steps; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions.Steps; - -internal sealed record AddLeasesTransactionStep : TransactionStepBase, IAddLeasesTransactionStep -{ - public ImmutableArray Leases { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/Steps/AppendCommandTransactionStep.cs b/src/EntityDb.Common/Transactions/Steps/AppendCommandTransactionStep.cs deleted file mode 100644 index f5b84f84..00000000 --- a/src/EntityDb.Common/Transactions/Steps/AppendCommandTransactionStep.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Transactions.Steps; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Transactions.Steps; - -internal sealed record AppendCommandTransactionStep : TransactionStepBase, IAppendCommandTransactionStep -{ - public object Command { get; init; } = default!; - public VersionNumber PreviousEntityVersionNumber { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/Steps/DeleteLeasesTransactionStep.cs b/src/EntityDb.Common/Transactions/Steps/DeleteLeasesTransactionStep.cs deleted file mode 100644 index 8d0e3ed4..00000000 --- a/src/EntityDb.Common/Transactions/Steps/DeleteLeasesTransactionStep.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Transactions.Steps; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions.Steps; - -internal sealed record DeleteLeasesTransactionStep : TransactionStepBase, IDeleteLeasesTransactionStep -{ - public ImmutableArray Leases { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/Steps/DeleteTagsTransactionStep.cs b/src/EntityDb.Common/Transactions/Steps/DeleteTagsTransactionStep.cs deleted file mode 100644 index a9b72d1f..00000000 --- a/src/EntityDb.Common/Transactions/Steps/DeleteTagsTransactionStep.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions.Steps; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions.Steps; - -internal sealed record DeleteTagsTransactionStep : TransactionStepBase, IDeleteTagsTransactionStep -{ - public ImmutableArray Tags { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/Steps/TagTransactionStep.cs b/src/EntityDb.Common/Transactions/Steps/TagTransactionStep.cs deleted file mode 100644 index 054b8994..00000000 --- a/src/EntityDb.Common/Transactions/Steps/TagTransactionStep.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions.Steps; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions.Steps; - -internal sealed record AddTagsTransactionStep : TransactionStepBase, IAddTagsTransactionStep -{ - public ImmutableArray Tags { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/Steps/TransactionStepBase.cs b/src/EntityDb.Common/Transactions/Steps/TransactionStepBase.cs deleted file mode 100644 index cea78ca2..00000000 --- a/src/EntityDb.Common/Transactions/Steps/TransactionStepBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Transactions.Steps; - -internal abstract record TransactionStepBase -{ - public Id EntityId { get; init; } - - public object Entity { get; init; } = default!; - - public VersionNumber EntityVersionNumber { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs new file mode 100644 index 00000000..20f58129 --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs @@ -0,0 +1,42 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Transactions.Subscribers.Processors; + +internal class EntitySnapshotTransactionCommandProcessor : ISnapshotTransactionCommandProcessor +{ + private readonly IEntityRepository _entityRepository; + private readonly SnapshotTransactionCommandProcessorCache _snapshotTransactionCommandProcessorCache; + + public EntitySnapshotTransactionCommandProcessor + ( + IEntityRepository entityRepository, + SnapshotTransactionCommandProcessorCache snapshotTransactionCommandProcessorCache + ) + { + _entityRepository = entityRepository; + _snapshotTransactionCommandProcessorCache = snapshotTransactionCommandProcessorCache; + } + + public async Task<(TEntity?, TEntity)?> GetSnapshots(ITransaction transaction, ITransactionCommand transactionCommand, CancellationToken cancellationToken) + { + if (transactionCommand is not ITransactionCommandWithSnapshot transactioncommandWithSnapshot || transactioncommandWithSnapshot.Snapshot is not TEntity nextSnapshot) + { + return null; + } + + var previousLatestPointer = transactionCommand.EntityId + + transactionCommand.EntityVersionNumber.Previous(); + + TEntity? previousLatestSnapshot = default; + + if (previousLatestPointer.VersionNumber != VersionNumber.MinValue) + { + previousLatestSnapshot = _snapshotTransactionCommandProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? + await _entityRepository.GetSnapshot(previousLatestPointer, cancellationToken); + } + + return (previousLatestSnapshot, nextSnapshot); + } +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs index b1016cde..5f3a6d14 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs @@ -34,20 +34,20 @@ public override async Task ProcessTransaction(ITransaction transaction, Cancella return; } - var snapshotTransactionStepProcessorCache = new SnapshotTransactionStepProcessorCache(); + var snapshotTransactionCommandProcessorCache = new SnapshotTransactionCommandProcessorCache(); - var snapshotStepProcessor = new EntitySnapshotTransactionStepProcessor + var snapshotCommandProcessor = new EntitySnapshotTransactionCommandProcessor ( entityRepository, - snapshotTransactionStepProcessorCache + snapshotTransactionCommandProcessorCache ); - await ProcessTransactionSteps + await ProcessTransactionCommands ( entityRepository.SnapshotRepository, - snapshotTransactionStepProcessorCache, + snapshotTransactionCommandProcessorCache, transaction, - snapshotStepProcessor, + snapshotCommandProcessor, cancellationToken ); } diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionStepProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionStepProcessor.cs deleted file mode 100644 index 9f28ccf2..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionStepProcessor.cs +++ /dev/null @@ -1,43 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal class EntitySnapshotTransactionStepProcessor : ISnapshotTransactionStepProcessor -{ - private readonly IEntityRepository _entityRepository; - private readonly SnapshotTransactionStepProcessorCache _snapshotTransactionStepProcessorCache; - - public EntitySnapshotTransactionStepProcessor - ( - IEntityRepository entityRepository, - SnapshotTransactionStepProcessorCache snapshotTransactionStepProcessorCache - ) - { - _entityRepository = entityRepository; - _snapshotTransactionStepProcessorCache = snapshotTransactionStepProcessorCache; - } - - public async Task<(TEntity?, TEntity)?> GetSnapshots(ITransaction transaction, ITransactionStep transactionStep, CancellationToken cancellationToken) - { - if (transactionStep is not IAppendCommandTransactionStep appendCommandTransactionStep || appendCommandTransactionStep.Entity is not TEntity nextSnapshot) - { - return null; - } - - var previousLatestPointer = appendCommandTransactionStep.EntityId + - appendCommandTransactionStep.PreviousEntityVersionNumber; - - TEntity? previousLatestSnapshot = default; - - if (previousLatestPointer.VersionNumber != VersionNumber.MinValue) - { - previousLatestSnapshot = _snapshotTransactionStepProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? - await _entityRepository.GetSnapshot(previousLatestPointer, cancellationToken); - } - - return (previousLatestSnapshot, nextSnapshot); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs new file mode 100644 index 00000000..c2e477ee --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs @@ -0,0 +1,8 @@ +using EntityDb.Abstractions.Transactions; + +namespace EntityDb.Common.Transactions.Subscribers.Processors; + +internal interface ISnapshotTransactionCommandProcessor +{ + Task<(TSnapshot?, TSnapshot)?> GetSnapshots(ITransaction transaction, ITransactionCommand transactionCommand, CancellationToken cancellationToken); +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionStepProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionStepProcessor.cs deleted file mode 100644 index 7a7ff34f..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionStepProcessor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal interface ISnapshotTransactionStepProcessor -{ - Task<(TSnapshot?, TSnapshot)?> GetSnapshots(ITransaction transaction, ITransactionStep transactionStep, CancellationToken cancellationToken); -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionStepProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs similarity index 58% rename from src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionStepProcessor.cs rename to src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs index e433c6d8..fb2d5a60 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionStepProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs @@ -1,54 +1,53 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Annotations; using EntityDb.Common.Projections; namespace EntityDb.Common.Transactions.Subscribers.Processors; -internal sealed class ProjectionSnapshotTransactionStepProcessor : ISnapshotTransactionStepProcessor +internal sealed class ProjectionSnapshotTransactionCommandProcessor : ISnapshotTransactionCommandProcessor where TProjection : IProjection { private readonly IProjectionRepository _projectionRepository; - private readonly SnapshotTransactionStepProcessorCache _snapshotTransactionStepProcessorCache; + private readonly SnapshotTransactionCommandProcessorCache _snapshotTransactionCommandProcessorCache; - public ProjectionSnapshotTransactionStepProcessor + public ProjectionSnapshotTransactionCommandProcessor ( IProjectionRepository projectionRepository, - SnapshotTransactionStepProcessorCache snapshotTransactionStepProcessorCache + SnapshotTransactionCommandProcessorCache snapshotTransactionCommandProcessorCache ) { _projectionRepository = projectionRepository; - _snapshotTransactionStepProcessorCache = snapshotTransactionStepProcessorCache; + _snapshotTransactionCommandProcessorCache = snapshotTransactionCommandProcessorCache; } public async Task<(TProjection?, TProjection)?> GetSnapshots ( ITransaction transaction, - ITransactionStep transactionStep, + ITransactionCommand transactionCommand, CancellationToken cancellationToken ) { - if (transactionStep is not IAppendCommandTransactionStep appendCommandTransactionStep) + if (transactionCommand is not ITransactionCommandWithSnapshot transactionCommandWithSnapshot) { return null; } - var projectionId = _projectionRepository.GetProjectionIdOrDefault(appendCommandTransactionStep.Entity); + var projectionId = _projectionRepository.GetProjectionIdOrDefault(transactionCommandWithSnapshot.Snapshot); if (projectionId is null) { return null; } - var previousLatestPointer = projectionId.Value + appendCommandTransactionStep.PreviousEntityVersionNumber; + var previousLatestPointer = projectionId.Value + transactionCommand.EntityVersionNumber.Previous(); TProjection? previousLatestSnapshot = default; if (previousLatestPointer.VersionNumber != VersionNumber.MinValue) { - previousLatestSnapshot = _snapshotTransactionStepProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? + previousLatestSnapshot = _snapshotTransactionCommandProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? await _projectionRepository.GetSnapshot(previousLatestPointer, cancellationToken); } @@ -57,9 +56,9 @@ await _projectionRepository.GetSnapshot(previousLatestPointer, ( transaction.Id, transaction.TimeStamp, - appendCommandTransactionStep.EntityId, - appendCommandTransactionStep.EntityVersionNumber, - appendCommandTransactionStep.Command + transactionCommand.EntityId, + transactionCommand.EntityVersionNumber, + transactionCommand.Command ); var nextSnapshot = diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs index 0912fec2..a483935e 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs @@ -35,20 +35,20 @@ public override async Task ProcessTransaction(ITransaction transaction, Cancella return; } - var snapshotTransactionStepProcessorCache = new SnapshotTransactionStepProcessorCache(); + var snapshotTransactionCommandProcessorCache = new SnapshotTransactionCommandProcessorCache(); - var projectionSnapshotTransactionStepProcessor = new ProjectionSnapshotTransactionStepProcessor + var projectionSnapshotTransactionCommandProcessor = new ProjectionSnapshotTransactionCommandProcessor ( projectionRepository, - snapshotTransactionStepProcessorCache + snapshotTransactionCommandProcessorCache ); - await ProcessTransactionSteps + await ProcessTransactionCommands ( projectionRepository.SnapshotRepository, - snapshotTransactionStepProcessorCache, + snapshotTransactionCommandProcessorCache, transaction, - projectionSnapshotTransactionStepProcessor, + projectionSnapshotTransactionCommandProcessor, cancellationToken ); } diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionStepProcessorCache.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs similarity index 87% rename from src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionStepProcessorCache.cs rename to src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs index c1b9d051..a4b2d1f1 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionStepProcessorCache.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs @@ -2,7 +2,7 @@ namespace EntityDb.Common.Transactions.Subscribers.Processors; -internal class SnapshotTransactionStepProcessorCache +internal class SnapshotTransactionCommandProcessorCache { private readonly Dictionary _cache = new(); diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs index b08dc096..5d5a2ffb 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs @@ -10,20 +10,20 @@ internal abstract class SnapshotTransactionProcessorBase : ITransacti { public abstract Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken); - protected static async Task ProcessTransactionSteps + protected static async Task ProcessTransactionCommands ( ISnapshotRepository snapshotRepository, - SnapshotTransactionStepProcessorCache snapshotTransactionStepProcessorCache, + SnapshotTransactionCommandProcessorCache snapshotTransactionCommandProcessorCache, ITransaction transaction, - ISnapshotTransactionStepProcessor snapshotTransactionStepProcessor, + ISnapshotTransactionCommandProcessor snapshotTransactionCommandProcessor, CancellationToken cancellationToken ) { var putQueue = new Dictionary(); - foreach (var transactionStep in transaction.Steps) + foreach (var transactionCommand in transaction.Commands) { - var snapshots = await snapshotTransactionStepProcessor.GetSnapshots(transaction, transactionStep, cancellationToken); + var snapshots = await snapshotTransactionCommandProcessor.GetSnapshots(transaction, transactionCommand, cancellationToken); if (snapshots is not var (previousLatestSnapshot, nextSnapshot)) { @@ -38,7 +38,7 @@ CancellationToken cancellationToken } else { - snapshotTransactionStepProcessorCache.PutSnapshot(snapshotId, nextSnapshot); + snapshotTransactionCommandProcessorCache.PutSnapshot(snapshotId, nextSnapshot); } if (nextSnapshot.ShouldRecord()) diff --git a/src/EntityDb.Common/Transactions/Transaction.cs b/src/EntityDb.Common/Transactions/Transaction.cs index 669116ca..1af6c8b8 100644 --- a/src/EntityDb.Common/Transactions/Transaction.cs +++ b/src/EntityDb.Common/Transactions/Transaction.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using System.Collections.Immutable; @@ -10,5 +9,5 @@ internal sealed record Transaction : ITransaction public Id Id { get; init; } public TimeStamp TimeStamp { get; init; } public object AgentSignature { get; init; } = default!; - public ImmutableArray Steps { get; init; } + public ImmutableArray Commands { get; init; } } diff --git a/src/EntityDb.Common/Transactions/TransactionCommand.cs b/src/EntityDb.Common/Transactions/TransactionCommand.cs new file mode 100644 index 00000000..3656ce1c --- /dev/null +++ b/src/EntityDb.Common/Transactions/TransactionCommand.cs @@ -0,0 +1,11 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Transactions; + +internal record TransactionCommand : ITransactionCommand +{ + public Id EntityId { get; init; } + public VersionNumber EntityVersionNumber { get; init; } + public object Command { get; init; } = default!; +} diff --git a/src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs b/src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs new file mode 100644 index 00000000..ce67e95b --- /dev/null +++ b/src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs @@ -0,0 +1,6 @@ +namespace EntityDb.Common.Transactions; + +internal record TransactionCommandWithSnapshot : TransactionCommand, ITransactionCommandWithSnapshot +{ + public object Snapshot { get; init; } = default!; +} diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 811f79d3..21afc843 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -32,7 +32,7 @@ ITransaction transaction { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityIds = transaction.Steps.Select(transactionStep => transactionStep.EntityId).Distinct() + EntityIds = transaction.Commands.Select(transactionCommand => transactionCommand.EntityId).Distinct() .ToArray(), DataType = transaction.AgentSignature.GetType().Name, Data = envelopeService.Serialize(transaction.AgentSignature) diff --git a/src/EntityDb.MongoDb/Documents/CommandDocument.cs b/src/EntityDb.MongoDb/Documents/CommandDocument.cs index 2be01095..076992f2 100644 --- a/src/EntityDb.MongoDb/Documents/CommandDocument.cs +++ b/src/EntityDb.MongoDb/Documents/CommandDocument.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.Common.Queries; @@ -29,7 +28,7 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - IAppendCommandTransactionStep appendCommandTransactionStep + ITransactionCommand transactionCommand ) { var documents = new[] @@ -38,10 +37,10 @@ IAppendCommandTransactionStep appendCommandTransactionStep { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = appendCommandTransactionStep.EntityId, - EntityVersionNumber = appendCommandTransactionStep.EntityVersionNumber, - DataType = appendCommandTransactionStep.Command.GetType().Name, - Data = envelopeService.Serialize(appendCommandTransactionStep.Command) + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, + DataType = transactionCommand.Command.GetType().Name, + Data = envelopeService.Serialize(transactionCommand.Command) } }; diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs index edc1f5a5..204fd403 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.Common.Queries; @@ -30,16 +30,17 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - IAddLeasesTransactionStep addLeasesTransactionStep + ITransactionCommand transactionCommand, + IAddLeasesCommand addLeasesCommand ) { - var leaseDocuments = addLeasesTransactionStep.Leases + var leaseDocuments = addLeasesCommand.GetLeases() .Select(insertLease => new LeaseDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = addLeasesTransactionStep.EntityId, - EntityVersionNumber = addLeasesTransactionStep.EntityVersionNumber, + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, DataType = insertLease.GetType().Name, Data = envelopeService.Serialize(insertLease), Scope = insertLease.Scope, @@ -73,11 +74,12 @@ leaseQuery.Options as MongoDbQueryOptions public static DeleteDocumentsCommand GetDeleteCommand ( - IDeleteLeasesTransactionStep deleteLeasesTransactionStep + ITransactionCommand transactionCommand, + IDeleteLeasesCommand deleteLeasesCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(deleteLeasesTransactionStep.EntityId, deleteLeasesTransactionStep.Leases); + new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases().ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDocument.cs index 181168b1..51713809 100644 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDocument.cs @@ -1,6 +1,6 @@ +using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.Common.Queries; @@ -29,16 +29,17 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - IAddTagsTransactionStep addTagsTransactionStep + ITransactionCommand transactionCommand, + IAddTagsCommand addTagsCommand ) { - var tagDocuments = addTagsTransactionStep.Tags + var tagDocuments = addTagsCommand.GetTags() .Select(insertTag => new TagDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = addTagsTransactionStep.EntityId, - EntityVersionNumber = addTagsTransactionStep.EntityVersionNumber, + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, DataType = insertTag.GetType().Name, Data = envelopeService.Serialize(insertTag), Label = insertTag.Label, @@ -71,10 +72,11 @@ tagQuery.Options as MongoDbQueryOptions public static DeleteDocumentsCommand GetDeleteCommand ( - IDeleteTagsTransactionStep deleteTagsTransactionStep + ITransactionCommand transactionCommand, + IDeleteTagsCommand deleteTagsCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(deleteTagsTransactionStep.EntityId, deleteTagsTransactionStep.Tags); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags().ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs index 7f84b54b..5f3b7ffd 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs @@ -1,9 +1,9 @@ using EntityDb.Abstractions.Annotations; +using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Leases; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; @@ -150,24 +150,19 @@ public async Task PutTransaction(ITransaction transaction, CancellationTok await PutAgentSignature(transaction, cancellationToken); - foreach (var transactionStep in transaction.Steps) + foreach (var transactionCommand in transaction.Commands) { cancellationToken.ThrowIfCancellationRequested(); - await (transactionStep switch - { - IAppendCommandTransactionStep appendCommandTransactionStep - => PutCommand(transaction, appendCommandTransactionStep, cancellationToken), - IAddLeasesTransactionStep addLeasesTransactionStep - => PutLeases(transaction, addLeasesTransactionStep, cancellationToken), - IAddTagsTransactionStep addTagsTransactionStep - => PutTags(transaction, addTagsTransactionStep, cancellationToken), - IDeleteLeasesTransactionStep deleteLeasesTransactionStep - => DeleteLeases(deleteLeasesTransactionStep, cancellationToken), - IDeleteTagsTransactionStep deleteTagsTransactionStep - => DeleteTags(deleteTagsTransactionStep, cancellationToken), - _ => throw new NotSupportedException() - }); + VersionZeroReservedException.ThrowIfZero(transactionCommand.EntityVersionNumber); + + var previousVersionNumber = await CommandDocument + .GetLastEntityVersionNumber(_mongoSession, transactionCommand.EntityId, cancellationToken); + + OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber.Next(), + transactionCommand.EntityVersionNumber); + + await PutCommand(transaction, transactionCommand, cancellationToken); } cancellationToken.ThrowIfCancellationRequested(); @@ -196,51 +191,39 @@ await AgentSignatureDocument .Execute(_mongoSession, cancellationToken); } - private async Task PutCommand(ITransaction transaction, IAppendCommandTransactionStep appendCommandTransactionStep, + private async Task PutCommand(ITransaction transaction, ITransactionCommand transactionCommand, CancellationToken cancellationToken) { - VersionZeroReservedException.ThrowIfZero(appendCommandTransactionStep.EntityVersionNumber); - - var previousVersionNumber = await CommandDocument - .GetLastEntityVersionNumber(_mongoSession, appendCommandTransactionStep.EntityId, cancellationToken); - - OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber, - appendCommandTransactionStep.PreviousEntityVersionNumber); - await CommandDocument - .GetInsertCommand(_envelopeService, transaction, appendCommandTransactionStep) + .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_mongoSession, cancellationToken); - } - private async Task PutLeases(ITransaction transaction, IAddLeasesTransactionStep addLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetInsertCommand(_envelopeService, transaction, addLeasesTransactionStep) - .Execute(_mongoSession, cancellationToken); - } + if (transactionCommand.Command is IAddLeasesCommand addLeasesCommand) + { + await LeaseDocument + .GetInsertCommand(_envelopeService, transaction, transactionCommand, addLeasesCommand) + .Execute(_mongoSession, cancellationToken); + } - private async Task PutTags(ITransaction transaction, IAddTagsTransactionStep addTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetInsertCommand(_envelopeService, transaction, addTagsTransactionStep) - .Execute(_mongoSession, cancellationToken); - } + if (transactionCommand.Command is IAddTagsCommand addTagsCommand) + { + await TagDocument + .GetInsertCommand(_envelopeService, transaction, transactionCommand, addTagsCommand) + .Execute(_mongoSession, cancellationToken); + } - private async Task DeleteLeases(IDeleteLeasesTransactionStep deleteLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetDeleteCommand(deleteLeasesTransactionStep) - .Execute(_mongoSession, cancellationToken); - } + if (transactionCommand.Command is IDeleteLeasesCommand deleteLeasesCommand) + { + await LeaseDocument + .GetDeleteCommand(transactionCommand, deleteLeasesCommand) + .Execute(_mongoSession, cancellationToken); + } - private async Task DeleteTags(IDeleteTagsTransactionStep deleteTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetDeleteCommand(deleteTagsTransactionStep) - .Execute(_mongoSession, cancellationToken); + if (transactionCommand.Command is IDeleteTagsCommand deleteTagsCommand) + { + await TagDocument + .GetDeleteCommand(transactionCommand, deleteTagsCommand) + .Execute(_mongoSession, cancellationToken); + } } } diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs index c2656b49..af33dc2d 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs @@ -42,8 +42,8 @@ ITransaction transaction { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityIds = transaction.Steps - .Select(transactionStep => transactionStep.EntityId) + EntityIds = transaction.Commands + .Select(transactionCommand => transactionCommand.EntityId) .Distinct() .ToArray(), DataType = transaction.AgentSignature.GetType().Name, diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs index bf63cb44..bf19aab1 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.Common.Queries; @@ -33,11 +32,11 @@ internal sealed record CommandDocument : DocumentBase, IEntityDocument GetInsert + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - IAppendCommandTransactionStep appendCommandTransactionStep + ITransactionCommand transactionCommand ) { return new InsertDocumentsCommand @@ -49,10 +48,10 @@ IAppendCommandTransactionStep appendCommandTransactionStep { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = appendCommandTransactionStep.EntityId, - EntityVersionNumber = appendCommandTransactionStep.EntityVersionNumber, - DataType = appendCommandTransactionStep.Command.GetType().Name, - Data = envelopeService.Serialize(appendCommandTransactionStep.Command) + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, + DataType = transactionCommand.Command.GetType().Name, + Data = envelopeService.Serialize(transactionCommand.Command) } } ); diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs index d6135e94..08b693d5 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Commands; +using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.Common.Queries; @@ -33,25 +33,26 @@ internal sealed record LeaseDocument : DocumentBase, IEntityDocument EntityIdDocumentReader { get; } = new LeaseEntityIdDocumentReader(); - public static InsertDocumentsCommand GetInsert + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - IAddLeasesTransactionStep addLeasesTransactionStep + ITransactionCommand transactionCommand, + IAddLeasesCommand addLeasesCommand ) { return new InsertDocumentsCommand ( TableName, - addLeasesTransactionStep.Leases + addLeasesCommand.GetLeases() .Select(insertLease => { return new LeaseDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = addLeasesTransactionStep.EntityId, - EntityVersionNumber = addLeasesTransactionStep.EntityVersionNumber, + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, Scope = insertLease.Scope, Label = insertLease.Label, Value = insertLease.Value, @@ -80,11 +81,11 @@ ILeaseQuery leaseQuery public static DeleteDocumentsCommand GetDeleteCommand ( - IDeleteLeasesTransactionStep deleteLeasesTransactionStep + ITransactionCommand transactionCommand, IDeleteLeasesCommand deleteLeasesCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(deleteLeasesTransactionStep.EntityId, deleteLeasesTransactionStep.Leases); + new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases().ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs index 9ca3baa2..bfdfa8df 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs @@ -1,6 +1,6 @@ +using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.Common.Queries; @@ -32,25 +32,26 @@ internal sealed record TagDocument : DocumentBase, IEntityDocument public static IDocumentReader EntityIdDocumentReader { get; } = new TagEntityIdDocumentReader(); - public static InsertDocumentsCommand GetInsert + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - IAddTagsTransactionStep addTagsTransactionStep + ITransactionCommand transactionCommand, + IAddTagsCommand addTagsCommand ) { return new InsertDocumentsCommand ( TableName, - addTagsTransactionStep.Tags + addTagsCommand.GetTags() .Select(insertTag => { return new TagDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = addTagsTransactionStep.EntityId, - EntityVersionNumber = addTagsTransactionStep.EntityVersionNumber, + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, Label = insertTag.Label, Value = insertTag.Value, DataType = insertTag.GetType().Name, @@ -78,10 +79,10 @@ ITagQuery tagQuery public static DeleteDocumentsCommand GetDeleteCommand ( - IDeleteTagsTransactionStep deleteTagsTransactionStep + ITransactionCommand transactionCommand, IDeleteTagsCommand deleteTagsCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(deleteTagsTransactionStep.EntityId, deleteTagsTransactionStep.Tags); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags().ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs index 922e0987..d1f62478 100644 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs +++ b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs @@ -1,9 +1,9 @@ using EntityDb.Abstractions.Annotations; +using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Leases; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; @@ -153,24 +153,19 @@ public async Task PutTransaction(ITransaction transaction, CancellationTok await PutAgentSignature(transaction, cancellationToken); - foreach (var transactionStep in transaction.Steps) + foreach (var transactionCommand in transaction.Commands) { cancellationToken.ThrowIfCancellationRequested(); - await (transactionStep switch - { - IAppendCommandTransactionStep appendCommandTransactionStep - => PutCommand(transaction, appendCommandTransactionStep, cancellationToken), - IAddLeasesTransactionStep addLeasesTransactionStep - => PutLeases(transaction, addLeasesTransactionStep, cancellationToken), - IAddTagsTransactionStep addTagsTransactionStep - => PutTags(transaction, addTagsTransactionStep, cancellationToken), - IDeleteLeasesTransactionStep deleteLeasesTransactionStep - => DeleteLeases(deleteLeasesTransactionStep, cancellationToken), - IDeleteTagsTransactionStep deleteTagsTransactionStep - => DeleteTags(deleteTagsTransactionStep, cancellationToken), - _ => throw new NotSupportedException() - }); + VersionZeroReservedException.ThrowIfZero(transactionCommand.EntityVersionNumber); + + var previousVersionNumber = await CommandDocument + .GetLastEntityVersionNumber(_sqlDbSession, transactionCommand.EntityId, cancellationToken); + + OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber.Next(), + transactionCommand.EntityVersionNumber); + + await PutCommand(transaction, transactionCommand, cancellationToken); } await _sqlDbSession.CommitTransaction(cancellationToken); @@ -197,51 +192,40 @@ await AgentSignatureDocument .Execute(_sqlDbSession, cancellationToken); } - private async Task PutCommand(ITransaction transaction, IAppendCommandTransactionStep appendCommandTransactionStep, + private async Task PutCommand(ITransaction transaction, ITransactionCommand transactionCommand, CancellationToken cancellationToken) { - VersionZeroReservedException.ThrowIfZero(appendCommandTransactionStep.EntityVersionNumber); - - var previousVersionNumber = await CommandDocument - .GetLastEntityVersionNumber(_sqlDbSession, appendCommandTransactionStep.EntityId, cancellationToken); - - OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber, - appendCommandTransactionStep.PreviousEntityVersionNumber); - await CommandDocument - .GetInsert(_envelopeService, transaction, appendCommandTransactionStep) + .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_sqlDbSession, cancellationToken); - } - private async Task PutLeases(ITransaction transaction, IAddLeasesTransactionStep addLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetInsert(_envelopeService, transaction, addLeasesTransactionStep) - .Execute(_sqlDbSession, cancellationToken); - } - private async Task PutTags(ITransaction transaction, IAddTagsTransactionStep addTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetInsert(_envelopeService, transaction, addTagsTransactionStep) - .Execute(_sqlDbSession, cancellationToken); - } + if (transactionCommand.Command is IAddLeasesCommand addLeasesCommand) + { + await LeaseDocument + .GetInsertCommand(_envelopeService, transaction, transactionCommand, addLeasesCommand) + .Execute(_sqlDbSession, cancellationToken); + } - private async Task DeleteLeases(IDeleteLeasesTransactionStep deleteLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetDeleteCommand(deleteLeasesTransactionStep) - .Execute(_sqlDbSession, cancellationToken); - } + if (transactionCommand.Command is IAddTagsCommand addTagsCommand) + { + await TagDocument + .GetInsertCommand(_envelopeService, transaction, transactionCommand, addTagsCommand) + .Execute(_sqlDbSession, cancellationToken); + } - private async Task DeleteTags(IDeleteTagsTransactionStep deleteTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetDeleteCommand(deleteTagsTransactionStep) - .Execute(_sqlDbSession, cancellationToken); + if (transactionCommand.Command is IDeleteLeasesCommand deleteLeasesCommand) + { + await LeaseDocument + .GetDeleteCommand(transactionCommand, deleteLeasesCommand) + .Execute(_sqlDbSession, cancellationToken); + } + + if (transactionCommand.Command is IDeleteTagsCommand deleteTagsCommand) + { + await TagDocument + .GetDeleteCommand(transactionCommand, deleteTagsCommand) + .Execute(_sqlDbSession, cancellationToken); + } } } diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 3e6bb5cb..141b40b9 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -3,7 +3,6 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; @@ -113,9 +112,10 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T .Setup(repository => repository.PutTransaction(It.IsAny(), It.IsAny())) .ReturnsAsync((ITransaction transaction, CancellationToken _) => { - foreach (var transactionStep in transaction.Steps) - if (transactionStep is IAppendCommandTransactionStep commandTransactionStep) - commands.Add(commandTransactionStep.Command); + foreach (var transactionCommand in transaction.Commands) + { + commands.Add(transactionCommand.Command); + } return true; }); diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs index 68db22cc..97291465 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs @@ -1,4 +1,7 @@ -using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.Commands; +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.Tags; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; @@ -18,4 +21,36 @@ public TestEntity Reduce(TestEntity entity) { return entity with { VersionNumber = entity.VersionNumber.Next() }; } +} + +public record AddLease(ILease Lease) : DoNothing, IAddLeasesCommand +{ + public IEnumerable GetLeases() + { + yield return Lease; + } +} + +public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesCommand +{ + public IEnumerable GetLeases() + { + yield return Lease; + } +} + +public record AddTag(ITag Tag) : DoNothing, IAddTagsCommand +{ + public IEnumerable GetTags() + { + yield return Tag; + } +} + +public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsCommand +{ + public IEnumerable GetTags() + { + yield return Tag; + } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs index 51423dc6..0eda39aa 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs @@ -1,10 +1,15 @@ -using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.Commands; +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.Tags; using EntityDb.Common.Tests.Implementations.Entities; +using EntityDb.Common.Tests.Implementations.Leases; using EntityDb.Common.Tests.Implementations.Projections; +using EntityDb.Common.Tests.Implementations.Tags; namespace EntityDb.Common.Tests.Implementations.Commands; -public record StoreNumber(ulong Number) : IReducer, IReducer +public record StoreNumber(ulong Number) : IReducer, IReducer, IAddLeasesCommand, IAddTagsCommand { public OneToOneProjection Reduce(OneToOneProjection projection) { @@ -21,4 +26,14 @@ public TestEntity Reduce(TestEntity entity) VersionNumber = entity.VersionNumber.Next() }; } + + public IEnumerable GetLeases() + { + yield return new CountLease(Number); + } + + public IEnumerable GetTags() + { + yield return new CountTag(Number); + } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs new file mode 100644 index 00000000..a677bf1b --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs @@ -0,0 +1,29 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Entities; +using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Transactions; + +namespace EntityDb.Common.Tests.Implementations.Seeders; + +public static class TransactionCommandSeeder +{ + public static IEnumerable CreateFromCommands(Id entityId, uint numCommands) + where TEntity : class, IEntity, ISnapshotWithTestLogic + { + for (var previousVersionNumber = new VersionNumber(0); + previousVersionNumber.Value < numCommands; + previousVersionNumber = previousVersionNumber.Next()) + { + var entityVersionNumber = previousVersionNumber.Next(); + + yield return new TransactionCommandWithSnapshot + { + EntityId = entityId, + Snapshot = TEntity.Construct(entityId).WithVersionNumber(entityVersionNumber), + EntityVersionNumber = entityVersionNumber, + Command = CommandSeeder.Create() + }; + } + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs index 93c036b2..46c904b1 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs @@ -1,56 +1,31 @@ using System.Collections.Immutable; using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Agents; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; using EntityDb.Common.Transactions; -using EntityDb.Common.Transactions.Steps; namespace EntityDb.Common.Tests.Implementations.Seeders; -public static class TransactionStepSeeder -{ - public static IEnumerable CreateFromCommands(Id entityId, uint numCommands) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - for (var previousVersionNumber = new VersionNumber(0); - previousVersionNumber.Value < numCommands; - previousVersionNumber = previousVersionNumber.Next()) - { - var entityVersionNumber = previousVersionNumber.Next(); - - yield return new AppendCommandTransactionStep - { - EntityId = entityId, - Entity = TEntity.Construct(entityId).WithVersionNumber(entityVersionNumber), - EntityVersionNumber = entityVersionNumber, - PreviousEntityVersionNumber = previousVersionNumber, - Command = CommandSeeder.Create() - }; - } - } -} - public static class TransactionSeeder { - public static ITransaction Create(params ITransactionStep[] transactionSteps) + public static ITransaction Create(params ITransactionCommand[] transactionCommands) { return new Transaction { Id = Id.NewId(), TimeStamp = TimeStamp.UtcNow, AgentSignature = new UnknownAgentSignature(new Dictionary()), - Steps = transactionSteps.ToImmutableArray() + Commands = transactionCommands.ToImmutableArray() }; } public static ITransaction Create(Id entityId, uint numCommands) where TEntity : class, IEntity, ISnapshotWithTestLogic { - var transactionSteps = TransactionStepSeeder.CreateFromCommands(entityId, numCommands).ToArray(); + var transactionCommands = TransactionCommandSeeder.CreateFromCommands(entityId, numCommands).ToArray(); - return Create(transactionSteps); + return Create(transactionCommands); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs index fdc3d3ab..ac3ad565 100644 --- a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Commands; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; @@ -95,17 +95,17 @@ private async Task // ACT var transaction = transactionBuilder - .Add(new Lease(default!, default!, default!)) + .Append(new AddLease(new Lease(default!, default!, default!))) .Build(default); // ASSERT - transaction.Steps.Length.ShouldBe(1); + transaction.Commands.Length.ShouldBe(1); - var leaseTransactionStep = - transaction.Steps[0].ShouldBeAssignableTo().ShouldNotBeNull(); + var addLeasesCommand = + transaction.Commands[0].Command.ShouldBeAssignableTo().ShouldNotBeNull(); - leaseTransactionStep.Leases.ShouldNotBeEmpty(); + addLeasesCommand.GetLeases().ShouldNotBeEmpty(); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( @@ -178,10 +178,7 @@ private async Task { var index = (int)(v.Value - 1); - var commandTransactionStep = transaction.Steps[index].ShouldBeAssignableTo() - .ShouldNotBeNull(); - - commandTransactionStep.EntityVersionNumber.ShouldBe(v); + transaction.Commands[index].EntityVersionNumber.ShouldBe(v); } } @@ -220,12 +217,9 @@ private async Task Generic_GivenExistingEntity_WhenAppendingNewCommand_ThenTrans // ASSERT - transaction.Steps.Length.ShouldBe(1); - - var commandTransactionStep = - transaction.Steps[0].ShouldBeAssignableTo().ShouldNotBeNull(); + transaction.Commands.Length.ShouldBe(1); - commandTransactionStep.Command.ShouldBeEquivalentTo(new DoNothing()); + transaction.Commands[0].Command.ShouldBeEquivalentTo(new DoNothing()); } [Theory] diff --git a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs b/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs index bb86ac11..bb08244e 100644 --- a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs @@ -4,7 +4,6 @@ using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Agents; using EntityDb.Common.Entities; @@ -435,8 +434,6 @@ private static async Task BuildTransaction foreach (var count in counts) { transactionBuilder.Append(new StoreNumber(count)); - transactionBuilder.Add(new CountLease(count)); - transactionBuilder.Add(new CountTag(count)); } var transaction = (transactionBuilder.Build(transactionId) as Transaction).ShouldNotBeNull(); @@ -576,9 +573,9 @@ private async Task Generic_GivenNonUniqueVersionNumbers_WhenInsertingCommands_Th transaction = transaction with { - Steps = Enumerable - .Repeat(transaction.Steps, repeatCount) - .SelectMany(steps => steps) + Commands = Enumerable + .Repeat(transaction.Commands, repeatCount) + .SelectMany(commands => commands) .ToImmutableArray() }; @@ -619,21 +616,21 @@ private async Task serviceCollection.AddSingleton(loggerFactory); }); - var transactionStepMock = new Mock(MockBehavior.Strict); + var transactionCommandMock = new Mock(MockBehavior.Strict); - transactionStepMock - .SetupGet(step => step.EntityId) + transactionCommandMock + .SetupGet(command => command.EntityId) .Returns(default(Id)); - transactionStepMock - .SetupGet(step => step.PreviousEntityVersionNumber) - .Returns(versionNumber); + transactionCommandMock + .SetupGet(command => command.EntityVersionNumber) + .Returns(versionNumber.Next()); - transactionStepMock - .SetupGet(step => step.EntityVersionNumber) + transactionCommandMock + .SetupGet(command => command.EntityVersionNumber) .Returns(versionNumber); - var transaction = TransactionSeeder.Create(transactionStepMock.Object, transactionStepMock.Object); + var transaction = TransactionSeeder.Create(transactionCommandMock.Object, transactionCommandMock.Object); await using var transactionRepository = await serviceScope.ServiceProvider .GetRequiredService() @@ -701,18 +698,13 @@ private async Task // ASSERT - firstTransaction.Steps.Length.ShouldBe(1); - secondTransaction.Steps.Length.ShouldBe(1); - - firstTransaction.Steps.ShouldAllBe(step => step.EntityId == entityId); - secondTransaction.Steps.ShouldAllBe(step => step.EntityId == entityId); + firstTransaction.Commands.Length.ShouldBe(1); + secondTransaction.Commands.Length.ShouldBe(1); - var firstCommandTransactionStep = firstTransaction.Steps[0] - .ShouldBeAssignableTo().ShouldNotBeNull(); - var secondCommandTransactionStep = secondTransaction.Steps[0] - .ShouldBeAssignableTo().ShouldNotBeNull(); + firstTransaction.Commands.ShouldAllBe(command => command.EntityId == entityId); + secondTransaction.Commands.ShouldAllBe(command => command.EntityId == entityId); - firstCommandTransactionStep.EntityVersionNumber.ShouldBe(secondCommandTransactionStep.EntityVersionNumber); + firstTransaction.Commands[0].EntityVersionNumber.ShouldBe(secondTransaction.Commands[0].EntityVersionNumber); firstTransactionInserted.ShouldBeTrue(); secondTransactionInserted.ShouldBeFalse(); @@ -739,8 +731,8 @@ private async Task Generic_GivenNonUniqueTags_WhenInsertingTagDocuments_ThenRetu .CreateForSingleEntity(default!, default); var transaction = transactionBuilder - .Add(tag) - .Add(tag) + .Append(new AddTag(tag)) + .Append(new AddTag(tag)) .Build(default); await using var transactionRepository = await serviceScope.ServiceProvider @@ -774,8 +766,8 @@ private async Task Generic_GivenNonUniqueLeases_WhenInsertingLeaseDocuments_Then .CreateForSingleEntity(default!, default); var transaction = transactionBuilder - .Add(lease) - .Add(lease) + .Append(new AddLease(lease)) + .Append(new AddLease(lease)) .Build(default); await using var transactionRepository = await serviceScope.ServiceProvider @@ -958,7 +950,7 @@ private async Task Generic_GivenEntityInsertedWithTags_WhenRemovingAllTags_ThenF .CreateRepository(TestSessionOptions.Write); var initialTransaction = transactionBuilder - .Add(tag) + .Append(new AddTag(tag)) .Build(Id.NewId()); var initialTransactionInserted = await transactionRepository.PutTransaction(initialTransaction); @@ -974,7 +966,7 @@ private async Task Generic_GivenEntityInsertedWithTags_WhenRemovingAllTags_ThenF var actualInitialTags = await transactionRepository.EnumerateTags(tagQuery).ToArrayAsync(); var finalTransaction = transactionBuilder - .Delete(tag) + .Append(new DeleteTag(tag)) .Build(Id.NewId()); var finalTransactionInserted = await transactionRepository.PutTransaction(finalTransaction); @@ -1017,7 +1009,7 @@ private async Task Generic_GivenEntityInsertedWithLeases_WhenRemovingAllLeases_T .CreateRepository(TestSessionOptions.Write); var initialTransaction = transactionBuilder - .Add(lease) + .Append(new AddLease(lease)) .Build(Id.NewId()); var initialTransactionInserted = await transactionRepository.PutTransaction(initialTransaction); @@ -1033,7 +1025,7 @@ private async Task Generic_GivenEntityInsertedWithLeases_WhenRemovingAllLeases_T var actualInitialLeases = await transactionRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); var finalTransaction = transactionBuilder - .Delete(lease) + .Append(new DeleteLease(lease)) .Build(Id.NewId()); var finalTransactionInserted = await transactionRepository.PutTransaction(finalTransaction); @@ -1088,12 +1080,9 @@ private async Task transactionInserted.ShouldBeTrue(); - transaction.Steps.Length.ShouldBe(1); + transaction.Commands.Length.ShouldBe(1); - var commandTransactionStep = - transaction.Steps[0].ShouldBeAssignableTo().ShouldNotBeNull(); - - commandTransactionStep.EntityVersionNumber.ShouldBe(new VersionNumber(1)); + transaction.Commands[0].EntityVersionNumber.ShouldBe(new VersionNumber(1)); newCommands.Length.ShouldBe(1); @@ -1148,12 +1137,9 @@ private async Task secondTransactionInserted.ShouldBeTrue(); - secondTransaction.Steps.Length.ShouldBe(1); - - var secondCommandTransactionStep = secondTransaction.Steps[0] - .ShouldBeAssignableTo().ShouldNotBeNull(); + secondTransaction.Commands.Length.ShouldBe(1); - secondCommandTransactionStep.EntityVersionNumber.ShouldBe(new VersionNumber(2)); + secondTransaction.Commands[0].EntityVersionNumber.ShouldBe(new VersionNumber(2)); newCommands.Length.ShouldBe(1); From c8be665cbef16283facb0e02d11e2d41380e1a78 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 20 Dec 2022 14:29:07 -0800 Subject: [PATCH 20/93] feat: rework projections - use ITransaction and ITransactionCommand as inputs to GetProjectionIdOrDefault and Reduce (gives access to command and agent signature) --- .../Projections/IProjectionRepository.cs | 7 ++- .../Projections/IProjection.cs | 29 ++++++---- .../Projections/ProjectionRepository.cs | 58 +++++++++++++++++-- .../Queries/GetAgentSignatures.cs | 25 ++++++++ ...tionSnapshotTransactionCommandProcessor.cs | 19 +----- .../Projections/OneToOneProjection.cs | 34 ++++++----- 6 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 src/EntityDb.Common/Queries/GetAgentSignatures.cs diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index 42ed6980..0400c0e3 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -30,9 +30,10 @@ public interface IProjectionRepository : IDisposableResource Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default); /// - /// Maps an entity to a projection id, or default if the entity does not map to this projection. + /// Maps a transaction and transaction step to a projection id, or default if the entity does not map to this projection. /// - /// The entity object. + /// The transaction + /// A command in the transaction /// The projection id for the entity, or default if none. - Id? GetProjectionIdOrDefault(object entity); + Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand); } diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs index 3706a13d..6be615d1 100644 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ b/src/EntityDb.Common/Projections/IProjection.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Annotations; using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Snapshots; @@ -12,23 +12,30 @@ namespace EntityDb.Common.Projections; public interface IProjection : ISnapshot { /// - /// Returns a new that incorporates the command for a particular entity id. + /// Returns a new that incorporates the transaction command. /// - /// The annotated command. - /// A new that incorporates . - TProjection Reduce(IEntityAnnotation annotatedCommand); + /// + /// + /// + TProjection Reduce(ITransaction transaction, ITransactionCommand transactionCommand); /// - /// Returns a that is used to load the rest of the state for the given projection pointer. + /// Returns a that finds transaction commands that need to be passed to the reducer. /// - /// A pointer to the projection. - /// A that is used to load the rest of the state for the given projection pointer. - ICommandQuery GetCommandQuery(Pointer projectionPointer); + /// A pointer to the desired projection state + /// The transaction repository, which can be used to locate new information + /// A cancellation token + /// A that is used to load the rest of the transaction commands for the given projection pointer. + /// + /// I would only recommend using the transaction repository to locate leases or tags, not commands or agent signatures. + /// + Task GetReducersQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken); /// /// Maps an entity to a projection id, or default if the entity does not map to this projection. /// - /// The entity object. + /// The transaction that could trigger a projection + /// The transaction command that could trigger a projection /// The projection id for the entity, or default if none. - static abstract Id? GetProjectionIdOrDefault(object entity); + static abstract Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand); } diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index a36af76d..29bff4ad 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,10 +1,15 @@ using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; +using EntityDb.Common.Queries; +using EntityDb.Common.Transactions; using Microsoft.Extensions.DependencyInjection; +using System.Collections.Immutable; +using System.Runtime.CompilerServices; namespace EntityDb.Common.Projections; @@ -26,9 +31,9 @@ public ProjectionRepository public ISnapshotRepository? SnapshotRepository { get; } - public Id? GetProjectionIdOrDefault(object entity) + public Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand) { - return TProjection.GetProjectionIdOrDefault(entity); + return TProjection.GetProjectionIdOrDefault(transaction, transactionCommand); } public async Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default) @@ -38,13 +43,16 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati TProjection.Construct(projectionPointer.Id) : TProjection.Construct(projectionPointer.Id); - var commandQuery = projection.GetCommandQuery(projectionPointer); + var newTransactionIdsQuery = await projection.GetReducersQuery(projectionPointer, TransactionRepository, cancellationToken); - var annotatedCommands = TransactionRepository.EnumerateAnnotatedCommands(commandQuery, cancellationToken); + var transactions = EnumerateTransactions(newTransactionIdsQuery, cancellationToken); - await foreach (var annotatedCommand in annotatedCommands) + await foreach (var transaction in transactions) { - projection = projection.Reduce(annotatedCommand); + foreach (var transactionCommand in transaction.Commands) + { + projection = projection.Reduce(transaction, transactionCommand); + } } if (!projectionPointer.IsSatisfiedBy(projection.GetVersionNumber())) @@ -68,4 +76,42 @@ public static IProjectionRepository Create(IServiceProvider service return ActivatorUtilities.CreateInstance>(serviceProvider, transactionRepository, snapshotRepository); } + + private async IAsyncEnumerable EnumerateTransactions(ICommandQuery commandQuery, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var allAnnotatedCommands = await TransactionRepository + .EnumerateAnnotatedCommands(commandQuery, cancellationToken) + .ToLookupAsync(annotatedCommand => annotatedCommand.TransactionId, cancellationToken); + + var transactionIds = allAnnotatedCommands + .SelectMany(annotatedCommands => annotatedCommands + .Select(annotatedCommand => annotatedCommand.TransactionId)) + .Distinct() + .ToArray(); + + var agentSignatureQuery = new GetAgentSignatures(transactionIds); + + var annotatedAgentSignatures = TransactionRepository + .EnumerateAnnotatedAgentSignatures(agentSignatureQuery, cancellationToken); + + await foreach (var annotatedAgentSignature in annotatedAgentSignatures) + { + var annotatedCommands = allAnnotatedCommands[annotatedAgentSignature.TransactionId]; + + yield return new Transaction + { + Id = annotatedAgentSignature.TransactionId, + TimeStamp = annotatedAgentSignature.TransactionTimeStamp, + AgentSignature = annotatedAgentSignature.Data, + Commands = annotatedCommands + .Select(annotatedCommand => new TransactionCommand + { + EntityId = annotatedCommand.EntityId, + EntityVersionNumber = annotatedCommand.EntityVersionNumber, + Command = annotatedCommand.Data + }) + .ToImmutableArray() + }; + } + } } diff --git a/src/EntityDb.Common/Queries/GetAgentSignatures.cs b/src/EntityDb.Common/Queries/GetAgentSignatures.cs new file mode 100644 index 00000000..7eeae27b --- /dev/null +++ b/src/EntityDb.Common/Queries/GetAgentSignatures.cs @@ -0,0 +1,25 @@ +using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Queries.FilterBuilders; +using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Queries; + +internal sealed record GetAgentSignatures(Id[] TransactionIds) : IAgentSignatureQuery +{ + public TFilter GetFilter(IAgentSignatureFilterBuilder builder) + { + return builder.TransactionIdIn(TransactionIds); + } + + public TSort? GetSort(IAgentSignatureSortBuilder builder) + { + return builder.TransactionTimeStamp(true); + } + + public int? Skip => null; + + public int? Take => null; + + public object? Options => null; +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs index fb2d5a60..69edf1da 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Annotations; using EntityDb.Common.Projections; namespace EntityDb.Common.Transactions.Subscribers.Processors; @@ -29,12 +28,7 @@ SnapshotTransactionCommandProcessorCache snapshotTransactionCommand CancellationToken cancellationToken ) { - if (transactionCommand is not ITransactionCommandWithSnapshot transactionCommandWithSnapshot) - { - return null; - } - - var projectionId = _projectionRepository.GetProjectionIdOrDefault(transactionCommandWithSnapshot.Snapshot); + var projectionId = _projectionRepository.GetProjectionIdOrDefault(transaction, transactionCommand); if (projectionId is null) { @@ -52,17 +46,8 @@ await _projectionRepository.GetSnapshot(previousLatestPointer, cancellationToken); } - var annotatedCommand = EntityAnnotation.CreateFromBoxedData - ( - transaction.Id, - transaction.TimeStamp, - transactionCommand.EntityId, - transactionCommand.EntityVersionNumber, - transactionCommand.Command - ); - var nextSnapshot = - (previousLatestSnapshot ?? TProjection.Construct(projectionId.Value)).Reduce(annotatedCommand); + (previousLatestSnapshot ?? TProjection.Construct(projectionId.Value)).Reduce(transaction, transactionCommand); return (previousLatestSnapshot, nextSnapshot); } diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 183be609..4917bd19 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,8 +1,8 @@ -using System.ComponentModel.DataAnnotations.Schema; -using EntityDb.Abstractions.Annotations; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Transactions; using EntityDb.Common.Projections; using EntityDb.Common.Queries; using EntityDb.Common.Tests.Implementations.Entities; @@ -47,16 +47,17 @@ public VersionNumber GetVersionNumber() return VersionNumber; } - public OneToOneProjection Reduce(IEntityAnnotation annotatedCommand) + public OneToOneProjection Reduce(ITransaction transaction, ITransactionCommand transactionCommand) { - return annotatedCommand switch + if (transactionCommand.Command is not IReducer reducer) { - IEntityAnnotation> reducer => reducer.Data.Reduce(this) with - { - LastEventAt = reducer.TransactionTimeStamp, - VersionNumber = reducer.EntityVersionNumber - }, - _ => throw new NotSupportedException() + throw new NotSupportedException(); + } + + return reducer.Reduce(this) with + { + LastEventAt = transaction.TimeStamp, + VersionNumber = transactionCommand.EntityVersionNumber }; } @@ -70,16 +71,19 @@ public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); } - public ICommandQuery GetCommandQuery(Pointer projectionPointer) + public Task GetReducersQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken) { - return new GetEntityCommandsQuery(projectionPointer, VersionNumber); + return Task.FromResult(new GetEntityCommandsQuery(projectionPointer, VersionNumber)); } - public static Id? GetProjectionIdOrDefault(object entity) + public static Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand) { - if (entity is TestEntity testEntity) return testEntity.Id; + if (transactionCommand is not ITransactionCommandWithSnapshot transactionCommandWithSnapshot || transactionCommandWithSnapshot.Snapshot is not TestEntity testEntity) + { + return null; + } - return null; + return testEntity.Id; } public static string RedisKeyNamespace => "one-to-one-projection"; From d6b865e796470616271229548ed9bb6c2725e88a Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 20 Dec 2022 14:36:12 -0800 Subject: [PATCH 21/93] refactor: change name --- src/EntityDb.Common/Projections/IProjection.cs | 2 +- src/EntityDb.Common/Projections/ProjectionRepository.cs | 2 +- .../Implementations/Projections/OneToOneProjection.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs index 6be615d1..82cba0d5 100644 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ b/src/EntityDb.Common/Projections/IProjection.cs @@ -29,7 +29,7 @@ public interface IProjection : ISnapshot /// /// I would only recommend using the transaction repository to locate leases or tags, not commands or agent signatures. /// - Task GetReducersQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken); + Task GetCommandQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken); /// /// Maps an entity to a projection id, or default if the entity does not map to this projection. diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index 29bff4ad..40cad67f 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -43,7 +43,7 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati TProjection.Construct(projectionPointer.Id) : TProjection.Construct(projectionPointer.Id); - var newTransactionIdsQuery = await projection.GetReducersQuery(projectionPointer, TransactionRepository, cancellationToken); + var newTransactionIdsQuery = await projection.GetCommandQuery(projectionPointer, TransactionRepository, cancellationToken); var transactions = EnumerateTransactions(newTransactionIdsQuery, cancellationToken); diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 4917bd19..91e40c5a 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -71,7 +71,7 @@ public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); } - public Task GetReducersQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken) + public Task GetCommandQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken) { return Task.FromResult(new GetEntityCommandsQuery(projectionPointer, VersionNumber)); } From 7161fc592c21263c4874a52cd444f0032112faa4 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 20 Dec 2022 17:45:46 -0800 Subject: [PATCH 22/93] refactor: RedisSnapshotSessionOptions and RedisSession do not need to be generic --- src/EntityDb.Redis/Sessions/RedisSession.cs | 10 +++++----- .../Sessions/RedisSnapshotSessionOptions.cs | 4 ++-- .../Snapshots/RedisSnapshotRepositoryFactory.cs | 8 ++++---- test/EntityDb.Common.Tests/TestsBase.cs | 8 ++++---- .../EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/EntityDb.Redis/Sessions/RedisSession.cs b/src/EntityDb.Redis/Sessions/RedisSession.cs index 78a24705..6876adf5 100644 --- a/src/EntityDb.Redis/Sessions/RedisSession.cs +++ b/src/EntityDb.Redis/Sessions/RedisSession.cs @@ -7,11 +7,11 @@ namespace EntityDb.Redis.Sessions; -internal sealed record RedisSession +internal sealed record RedisSession ( - ILogger> Logger, + ILogger Logger, IDatabase Database, - RedisSnapshotSessionOptions Options + RedisSnapshotSessionOptions Options ) : DisposableResourceBaseRecord, IRedisSession { public async Task Insert(Pointer snapshotPointer, RedisValue redisValue) @@ -139,9 +139,9 @@ public static IRedisSession Create ( IServiceProvider serviceProvider, IDatabase database, - RedisSnapshotSessionOptions options + RedisSnapshotSessionOptions options ) { - return ActivatorUtilities.CreateInstance>(serviceProvider, database, options); + return ActivatorUtilities.CreateInstance(serviceProvider, database, options); } } diff --git a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs index 65c27f7b..0edfe9ea 100644 --- a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs @@ -7,7 +7,7 @@ namespace EntityDb.Redis.Sessions; /// /// Configuration options for the Redis implementation of . /// -public class RedisSnapshotSessionOptions +public class RedisSnapshotSessionOptions { /// /// A connection string that is compatible with @@ -34,6 +34,6 @@ public class RedisSnapshotSessionOptions [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { - return $"{nameof(RedisSnapshotSessionOptions)}<{typeof(TSnapshot).Name}"; + return $"{nameof(RedisSnapshotSessionOptions)}"; } } diff --git a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs index 7426383f..c77070e9 100644 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs +++ b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs @@ -14,14 +14,14 @@ internal class RedisSnapshotRepositoryFactory : DisposableResourceBas { private readonly ConnectionMultiplexerFactory _connectionMultiplexerFactory; private readonly IEnvelopeService _envelopeService; - private readonly IOptionsFactory> _optionsFactory; + private readonly IOptionsFactory _optionsFactory; private readonly IServiceProvider _serviceProvider; public RedisSnapshotRepositoryFactory ( IServiceProvider serviceProvider, ConnectionMultiplexerFactory connectionMultiplexerFactory, - IOptionsFactory> optionsFactory, + IOptionsFactory optionsFactory, IEnvelopeService envelopeService ) { @@ -48,13 +48,13 @@ public async Task> CreateRepository(string snapsh } - private async Task CreateSession(RedisSnapshotSessionOptions options, + private async Task CreateSession(RedisSnapshotSessionOptions options, CancellationToken cancellationToken) { var connectionMultiplexer = await _connectionMultiplexerFactory.CreateConnectionMultiplexer(options.ConnectionString, cancellationToken); - return RedisSession.Create(_serviceProvider, connectionMultiplexer.GetDatabase(), options); + return RedisSession.Create(_serviceProvider, connectionMultiplexer.GetDatabase(), options); } public static RedisSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 6759b9d9..66bae696 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -197,24 +197,24 @@ private static SnapshotAdder RedisSnapshotAdder() serviceCollection.AddRedisSnapshots(true); - serviceCollection.ConfigureAll>(options => + serviceCollection.ConfigureAll(options => { options.ConnectionString = databaseContainerFixture!.RedisContainer.ConnectionString; options.KeyNamespace = TSnapshot.RedisKeyNamespace; }); - serviceCollection.Configure>(TestSessionOptions.Write, options => + serviceCollection.Configure(TestSessionOptions.Write, options => { options.ReadOnly = false; }); - serviceCollection.Configure>(TestSessionOptions.ReadOnly, options => + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { options.ReadOnly = true; options.SecondaryPreferred = false; }); - serviceCollection.Configure>(TestSessionOptions.ReadOnlySecondaryPreferred, options => + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => { options.ReadOnly = true; options.SecondaryPreferred = true; diff --git a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs index eb341fc1..9c0a0bf8 100644 --- a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs +++ b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs @@ -17,7 +17,7 @@ public async Task WhenExecutingWriteMethods_ThenThrow() { // ARRANGE - var readOnlyRedisSession = new RedisSession(default!, default!, new RedisSnapshotSessionOptions + var readOnlyRedisSession = new RedisSession(default!, default!, new RedisSnapshotSessionOptions { ReadOnly = true }); From 3b38f91c4773cc354790dd4ad6a640a350fa2b68 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Wed, 21 Dec 2022 12:23:34 -0800 Subject: [PATCH 23/93] feat: connection string slot --- .../Sessions/EntityFrameworkSnapshotSessionOptions.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 1ed25e2f..3cdbd69e 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -8,6 +8,13 @@ namespace EntityDb.EntityFramework.Sessions; /// public class EntityFrameworkSnapshotSessionOptions { + /// + /// This property is not used by the package. It only provides a convenient way to access + /// the connection string using IOptions, which does not appear to be a convienent thing + /// to do in vanilla Enitity Framework. + /// + public string ConnectionString { get; set; } = default!; + /// /// If true, indicates the agent only intends to execute queries. /// From ff384813de3d19ab4ec8c19aca549562859b137a Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 31 Dec 2022 01:08:06 -0800 Subject: [PATCH 24/93] feat: transaction reprocessor queue --- .../Extensions/ServiceCollectionExtensions.cs | 59 ++++++++++----- .../TransactionRepositoryExtensions.cs | 55 ++++++++++++++ .../Projections/ProjectionRepository.cs | 41 +--------- ...> BufferBlockTransactionProcessorQueue.cs} | 17 ++++- ...s => TestModeTransactionProcessorQueue.cs} | 4 +- .../EntitySnapshotTransactionProcessor.cs | 2 +- .../Processors/ITransactionProcessor.cs | 6 ++ .../ProjectionSnapshotTransactionProcessor.cs | 2 +- .../SnapshotTransactionProcessorBase.cs | 7 ++ .../BufferBlockTransactionReprocessorQueue.cs | 52 +++++++++++++ .../ITransactionReprocessorQueue.cs | 15 ++++ .../TestModeTransactionReprocessorQueue.cs | 18 +++++ .../TransactionReprocessorQueueBase.cs | 74 +++++++++++++++++++ .../IReprocessTransactionRequest.cs | 39 ++++++++++ 14 files changed, 324 insertions(+), 67 deletions(-) create mode 100644 src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs rename src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/{BufferBlockTransactionQueue.cs => BufferBlockTransactionProcessorQueue.cs} (61%) rename src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/{TestModeTransactionQueue.cs => TestModeTransactionProcessorQueue.cs} (71%) create mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs create mode 100644 src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 35c3183c..df3cd4a7 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -9,6 +9,7 @@ using EntityDb.Common.Transactions.Subscribers; using EntityDb.Common.Transactions.Subscribers.ProcessorQueues; using EntityDb.Common.Transactions.Subscribers.Processors; +using EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; using EntityDb.Common.TypeResolvers; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; @@ -22,24 +23,6 @@ namespace EntityDb.Common.Extensions; /// public static class ServiceCollectionExtensions { - private static void AddTestModeTransactionProcessorQueue(this IServiceCollection serviceCollection) - where TTransactionProcessor : ITransactionProcessor - { - serviceCollection.AddSingleton, TestModeTransactionQueue>(); - } - - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - private static void AddBufferBlockTransactionProcessorQueue(this IServiceCollection serviceCollection) - where TTransactionProcessor : ITransactionProcessor - { - serviceCollection.AddSingleton>(); - - serviceCollection.AddSingleton>(serviceProvider => serviceProvider.GetRequiredService>()); - - serviceCollection.AddHostedService(serviceProvider => - serviceProvider.GetRequiredService>()); - } - internal static void Add(this IServiceCollection serviceCollection, ServiceLifetime serviceLifetime, Func serviceFactory) where TService : class @@ -82,11 +65,41 @@ public static void AddTransactionProcessorSubscriber(this if (testMode) { - serviceCollection.AddTestModeTransactionProcessorQueue(); + serviceCollection.AddSingleton, TestModeTransactionProcessorQueue>(); } else { - serviceCollection.AddBufferBlockTransactionProcessorQueue(); + serviceCollection.AddSingleton>(); + + serviceCollection.AddSingleton>(serviceProvider => serviceProvider.GetRequiredService>()); + + serviceCollection.AddHostedService(serviceProvider => + serviceProvider.GetRequiredService>()); + } + } + + /// + /// Registers a transaction reprocessor queue. For test mode, the queue is not actually a queue and will + /// immediately reprocess transactions. For non-test mode, the queue used a . + /// + /// The service collection. + /// Wether or not to run in test mode. + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static void AddTransactionReprocessorQueue(this IServiceCollection serviceCollection, bool testMode = false) + { + if (testMode) + { + serviceCollection.AddSingleton(); + } + else + { + serviceCollection.AddSingleton(); + + serviceCollection.AddHostedService(serviceProvider => + serviceProvider.GetRequiredService()); + + serviceCollection.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); } } @@ -159,6 +172,9 @@ public static void AddEntity(this IServiceCollection serviceCollection) /// The agent's intent for the snapshot repository. /// If true then snapshots will be synchronously recorded. /// The type of the entity. + /// + /// Under the hood, this registers an with equal to EntitySnapshot<> + /// public static void AddEntitySnapshotTransactionSubscriber(this IServiceCollection serviceCollection, string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false) where TEntity : IEntity @@ -188,6 +204,9 @@ public static void AddProjection( /// The agent's intent for the snapshot repository. /// If true then snapshots will be synchronously recorded. /// The type of the projection. + /// + /// Under the hood, this registers an with equal to ProjectionSnapshot<> + /// public static void AddProjectionSnapshotTransactionSubscriber( this IServiceCollection serviceCollection, string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false) diff --git a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs new file mode 100644 index 00000000..6dea67e3 --- /dev/null +++ b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs @@ -0,0 +1,55 @@ +using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Queries; +using EntityDb.Common.Transactions; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace EntityDb.Common.Extensions +{ + internal static class TransactionRepositoryExtensions + { + public static async IAsyncEnumerable EnumerateTransactions(this ITransactionRepository transactionRepository, ICommandQuery commandQuery, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var allAnnotatedCommands = await transactionRepository + .EnumerateAnnotatedCommands(commandQuery, cancellationToken) + .ToLookupAsync(annotatedCommand => annotatedCommand.TransactionId, cancellationToken); + + var transactionIds = allAnnotatedCommands + .SelectMany(annotatedCommands => annotatedCommands + .Select(annotatedCommand => annotatedCommand.TransactionId)) + .Distinct() + .ToArray(); + + var agentSignatureQuery = new GetAgentSignatures(transactionIds); + + var annotatedAgentSignatures = transactionRepository + .EnumerateAnnotatedAgentSignatures(agentSignatureQuery, cancellationToken); + + await foreach (var annotatedAgentSignature in annotatedAgentSignatures) + { + var annotatedCommands = allAnnotatedCommands[annotatedAgentSignature.TransactionId]; + + yield return new Transaction + { + Id = annotatedAgentSignature.TransactionId, + TimeStamp = annotatedAgentSignature.TransactionTimeStamp, + AgentSignature = annotatedAgentSignature.Data, + Commands = annotatedCommands + .Select(annotatedCommand => new TransactionCommand + { + EntityId = annotatedCommand.EntityId, + EntityVersionNumber = annotatedCommand.EntityVersionNumber, + Command = annotatedCommand.Data + }) + .ToImmutableArray() + }; + } + } + } +} diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index 40cad67f..e2c93983 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -5,6 +5,7 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; +using EntityDb.Common.Extensions; using EntityDb.Common.Queries; using EntityDb.Common.Transactions; using Microsoft.Extensions.DependencyInjection; @@ -45,7 +46,7 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati var newTransactionIdsQuery = await projection.GetCommandQuery(projectionPointer, TransactionRepository, cancellationToken); - var transactions = EnumerateTransactions(newTransactionIdsQuery, cancellationToken); + var transactions = TransactionRepository.EnumerateTransactions(newTransactionIdsQuery, cancellationToken); await foreach (var transaction in transactions) { @@ -76,42 +77,4 @@ public static IProjectionRepository Create(IServiceProvider service return ActivatorUtilities.CreateInstance>(serviceProvider, transactionRepository, snapshotRepository); } - - private async IAsyncEnumerable EnumerateTransactions(ICommandQuery commandQuery, [EnumeratorCancellation] CancellationToken cancellationToken) - { - var allAnnotatedCommands = await TransactionRepository - .EnumerateAnnotatedCommands(commandQuery, cancellationToken) - .ToLookupAsync(annotatedCommand => annotatedCommand.TransactionId, cancellationToken); - - var transactionIds = allAnnotatedCommands - .SelectMany(annotatedCommands => annotatedCommands - .Select(annotatedCommand => annotatedCommand.TransactionId)) - .Distinct() - .ToArray(); - - var agentSignatureQuery = new GetAgentSignatures(transactionIds); - - var annotatedAgentSignatures = TransactionRepository - .EnumerateAnnotatedAgentSignatures(agentSignatureQuery, cancellationToken); - - await foreach (var annotatedAgentSignature in annotatedAgentSignatures) - { - var annotatedCommands = allAnnotatedCommands[annotatedAgentSignature.TransactionId]; - - yield return new Transaction - { - Id = annotatedAgentSignature.TransactionId, - TimeStamp = annotatedAgentSignature.TransactionTimeStamp, - AgentSignature = annotatedAgentSignature.Data, - Commands = annotatedCommands - .Select(annotatedCommand => new TransactionCommand - { - EntityId = annotatedCommand.EntityId, - EntityVersionNumber = annotatedCommand.EntityVersionNumber, - Command = annotatedCommand.Data - }) - .ToImmutableArray() - }; - } - } } diff --git a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs similarity index 61% rename from src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionQueue.cs rename to src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs index 662ccc10..79ebabb9 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionQueue.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs @@ -8,14 +8,14 @@ namespace EntityDb.Common.Transactions.Subscribers.ProcessorQueues; [ExcludeFromCodeCoverage(Justification = "Not used in tests.")] -internal class BufferBlockTransactionQueue : BackgroundService, ITransactionProcessorQueue +internal class BufferBlockTransactionProcessorQueue : BackgroundService, ITransactionProcessorQueue where TTransactionProcessor : ITransactionProcessor { private readonly BufferBlock _transactionQueue = new(); - private readonly ILogger> _logger; + private readonly ILogger> _logger; private readonly TTransactionProcessor _transactionProcessor; - public BufferBlockTransactionQueue(ILogger> logger, TTransactionProcessor transactionProcessor) + public BufferBlockTransactionProcessorQueue(ILogger> logger, TTransactionProcessor transactionProcessor) { _logger = logger; _transactionProcessor = transactionProcessor; @@ -30,11 +30,20 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (await _transactionQueue.OutputAvailableAsync(stoppingToken)) { + var transaction = await _transactionQueue.ReceiveAsync(stoppingToken); + + using var _ = _logger.BeginScope(new KeyValuePair[] + { + new("TransactionId", transaction.Id.Value) + }); + try { - var transaction = await _transactionQueue.ReceiveAsync(stoppingToken); + _logger.LogDebug("Started processing transaction"); await _transactionProcessor.ProcessTransaction(transaction, stoppingToken); + + _logger.LogDebug("Finished processing transaction"); } catch (Exception exception) { diff --git a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs similarity index 71% rename from src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionQueue.cs rename to src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs index 24ce7a94..02b8e8fd 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionQueue.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs @@ -3,12 +3,12 @@ namespace EntityDb.Common.Transactions.Subscribers.ProcessorQueues; -internal class TestModeTransactionQueue : ITransactionProcessorQueue +internal class TestModeTransactionProcessorQueue : ITransactionProcessorQueue where TTransactionProcessor : ITransactionProcessor { private readonly TTransactionProcessor _transactionProcessor; - public TestModeTransactionQueue(TTransactionProcessor transactionProcessor) + public TestModeTransactionProcessorQueue(TTransactionProcessor transactionProcessor) { _transactionProcessor = transactionProcessor; } diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs index 5f3a6d14..861ef094 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs @@ -17,7 +17,7 @@ public EntitySnapshotTransactionProcessor IEntityRepositoryFactory entityRepositoryFactory, string transactionSessionOptionsName, string snapshotSessionOptionsName - ) + ) : base($"EntitySnapshot<{typeof(TEntity).Name}>") { _entityRepositoryFactory = entityRepositoryFactory; _transactionSessionOptionsName = transactionSessionOptionsName; diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs index 09cffee9..d983df98 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Transactions.Subscribers.Reprocessors; namespace EntityDb.Common.Transactions.Subscribers.Processors; @@ -7,6 +8,11 @@ namespace EntityDb.Common.Transactions.Subscribers.Processors; /// public interface ITransactionProcessor { + /// + /// Used for targeted reprocessing with . + /// + string Identifier { get; } + /// /// Defines the procedure for processing a given transaction. /// diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs index a483935e..fb72952e 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs @@ -18,7 +18,7 @@ public ProjectionSnapshotTransactionProcessor IProjectionRepositoryFactory projectionRepositoryFactory, string transactionSessionOptionsName, string snapshotSessionOptionsName - ) + ) : base($"ProjectionSnapshot<{typeof(TProjection).Name}>") { _projectionRepositoryFactory = projectionRepositoryFactory; _transactionSessionOptionsName = transactionSessionOptionsName; diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs index 5d5a2ffb..ee010a4f 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs @@ -8,6 +8,13 @@ namespace EntityDb.Common.Transactions.Subscribers.Processors; internal abstract class SnapshotTransactionProcessorBase : ITransactionProcessor where TSnapshot : ISnapshot { + public string Identifier { get; } + + protected SnapshotTransactionProcessorBase(string identifier) + { + Identifier = identifier; + } + public abstract Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken); protected static async Task ProcessTransactionCommands diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs new file mode 100644 index 00000000..23f49616 --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs @@ -0,0 +1,52 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Transactions.Subscribers.Processors; +using EntityDb.Common.Transactions.Subscribers.Reprocessors; +using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks.Dataflow; + +namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal class BufferBlockTransactionReprocessorQueue : TransactionReprocessorQueueBase, ITransactionReprocessorQueue +{ + private readonly BufferBlock _bufferBlock = new(); + private readonly ILogger _logger; + private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly IEnumerable _transactionProcessors; + + public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) : base(logger, transactionRepositoryFactory, transactionProcessors) + { + _logger = logger; + _transactionRepositoryFactory = transactionRepositoryFactory; + _transactionProcessors = transactionProcessors; + } + + public void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest) + { + _bufferBlock.Post(reprocessTransactionsRequest); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (await _bufferBlock.OutputAvailableAsync(stoppingToken)) + { + var reprocessTransactionsRequest = await _bufferBlock.ReceiveAsync(stoppingToken); + + using var _ = _logger.BeginScope(reprocessTransactionsRequest.ToLogScopeState()); + + try + { + _logger.LogDebug("Started reprocessing transactions"); + + await Process(reprocessTransactionsRequest, stoppingToken); + + _logger.LogDebug("Finished reprocessing transactions"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess transactions"); + } + } + } +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs new file mode 100644 index 00000000..d41079bc --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs @@ -0,0 +1,15 @@ +using EntityDb.Common.Transactions.Subscribers.Reprocessors; + +namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; + +/// +/// A queue that reprocesses transactions +/// +public interface ITransactionReprocessorQueue +{ + /// + /// Enqueues the request to reprocess transactions + /// + /// + void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest); +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs new file mode 100644 index 00000000..c37587c0 --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Transactions.Subscribers.Processors; +using EntityDb.Common.Transactions.Subscribers.Reprocessors; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; + +internal class TestModeTransactionReprocessorQueue : TransactionReprocessorQueueBase, ITransactionReprocessorQueue +{ + public TestModeTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) : base(logger, transactionRepositoryFactory, transactionProcessors) + { + } + + public void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest) + { + Task.Run(() => Process(reprocessTransactionsRequest, default)); + } +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs new file mode 100644 index 00000000..84a87489 --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs @@ -0,0 +1,74 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Extensions; +using EntityDb.Common.Transactions.Subscribers.Processors; +using EntityDb.Common.Transactions.Subscribers.Reprocessors; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; + +internal abstract class TransactionReprocessorQueueBase : BackgroundService +{ + private readonly ILogger _logger; + private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly IEnumerable _transactionProcessors; + + protected TransactionReprocessorQueueBase(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) + { + _logger = logger; + _transactionRepositoryFactory = transactionRepositoryFactory; + _transactionProcessors = transactionProcessors; + } + + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + // I don't like that I have to do this, but it's the only decent way + // to share the Process method between BufferBlock and TestMode implementations + throw new NotSupportedException(); + } + + protected async Task Process(IReprocessTransactionsRequest reprocessTransactionsRequest, CancellationToken cancellationToken) + { + using var outerScope = _logger.BeginScope(reprocessTransactionsRequest.ToLogScopeState()); + + try + { + _logger.LogDebug("Started reprocessing transactions"); + + var transactionProcessor = _transactionProcessors + .Single(transactionProcessor => transactionProcessor.Identifier == reprocessTransactionsRequest.TransactionProcessorIdentifier); + + await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(reprocessTransactionsRequest.TransactionSessionOptionsName, cancellationToken); + + var transactions = transactionRepository.EnumerateTransactions(reprocessTransactionsRequest.CommandQuery, cancellationToken); + + await foreach (var transaction in transactions) + { + using var innerScope = _logger.BeginScope(new KeyValuePair[] + { + new("TransactionId", transaction.Id.Value), + }); + + try + { + await transactionProcessor.ProcessTransaction(transaction, cancellationToken); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess transaction"); + + if (reprocessTransactionsRequest.BreakOnThrow) + { + break; + } + } + } + + _logger.LogDebug("Finished reprocessing transactions"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess transactions"); + } + } +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs b/src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs new file mode 100644 index 00000000..13ee77a2 --- /dev/null +++ b/src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs @@ -0,0 +1,39 @@ +using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Transactions.Subscribers.Processors; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Transactions.Subscribers.Reprocessors; + +/// +/// Represents a request for a to reprocess transactions. +/// +public interface IReprocessTransactionsRequest +{ + /// + /// The name of the transaction session options passed to + /// + string TransactionSessionOptionsName { get; } + + /// + /// Determines which transaction processor needs to reprocess the transactions, based on the value of . + /// + string TransactionProcessorIdentifier { get; } + + /// + /// Determines which transactions need to be reprocessed. + /// + ICommandQuery CommandQuery { get; } + + /// + /// If true, then stop executing this request when the transaction processor throws an exception. + /// Otherwise, execute this request for all matching transactions. + /// + bool BreakOnThrow { get; } + + /// + /// Converts this request into a log-friendly format. + /// + /// The object that will be passed to + object ToLogScopeState(); +} From a00f92ae460176371ee501990009f883b007296d Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 31 Dec 2022 01:30:53 -0800 Subject: [PATCH 25/93] fix: need to register the ITransactionProcessor --- src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index df3cd4a7..5b6bce14 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -59,6 +59,8 @@ public static void AddTransactionProcessorSubscriber(this { serviceCollection.AddSingleton(transactionProcessorFactory.Invoke); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton>(); serviceCollection.AddSingleton>(); From 9a02723b66c416031a9f47a81dfcc6744c7abd50 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 31 Dec 2022 01:32:47 -0800 Subject: [PATCH 26/93] fix: registered ITransactionProcessor the wrong way --- src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 5b6bce14..4e9cd20c 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -59,7 +59,7 @@ public static void AddTransactionProcessorSubscriber(this { serviceCollection.AddSingleton(transactionProcessorFactory.Invoke); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); serviceCollection.AddSingleton>(); From d6f4f80d053741b74bcaaf5da26d3caa38735332 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 31 Dec 2022 02:02:37 -0800 Subject: [PATCH 27/93] clean: remove redundant try/catch and logging --- .../BufferBlockTransactionReprocessorQueue.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs index 23f49616..85936b28 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs @@ -33,20 +33,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var reprocessTransactionsRequest = await _bufferBlock.ReceiveAsync(stoppingToken); - using var _ = _logger.BeginScope(reprocessTransactionsRequest.ToLogScopeState()); - - try - { - _logger.LogDebug("Started reprocessing transactions"); - - await Process(reprocessTransactionsRequest, stoppingToken); - - _logger.LogDebug("Finished reprocessing transactions"); - } - catch (Exception exception) - { - _logger.LogError(exception, "Failed to reprocess transactions"); - } + await Process(reprocessTransactionsRequest, stoppingToken); } } } From 00201bb2e40fe571c11a6d52669fbf5ed8e2581e Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Fri, 25 Aug 2023 16:49:53 -0700 Subject: [PATCH 28/93] Update global.json --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 32cf95ef..ceb31304 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100", + "version": "7.0.201", "allowPrerelease": false, "rollForward": "disable" } From 105eb0b55156bf42596ad6e70ccde129922f130b Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Fri, 25 Aug 2023 16:49:53 -0700 Subject: [PATCH 29/93] chore: include debug logs --- test/EntityDb.Common.Tests/TestsBase.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 6759b9d9..57cd6e2f 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -357,11 +357,16 @@ protected IServiceScope CreateServiceScope(Action? configure serviceCollection.AddLogging(loggingBuilder => { - loggingBuilder.AddProvider(new XunitTestOutputLoggerProvider(_testOutputHelperAccessor)); + loggingBuilder.AddProvider(new XunitTestOutputLoggerProvider(_testOutputHelperAccessor, (_,_) => true)); loggingBuilder.AddDebug(); loggingBuilder.AddSimpleConsole(options => { options.IncludeScopes = true; }); }); + serviceCollection.Configure(x => + { + x.MinLevel = LogLevel.Debug; + }); + startup.AddServices(serviceCollection); if (_databaseContainerFixture != null) From 3340585dfb06a47e96ff96c30f1717ba319b8439 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Fri, 25 Aug 2023 16:49:53 -0700 Subject: [PATCH 30/93] refactor: SnapshotReference _has one_ Snapshot instead of _owns_ snapshot --- .../Extensions/ServiceCollectionExtensions.cs | 4 +- .../Sessions/EntityFrameworkSession.cs | 70 +++++++++++++++---- ...ntityFrameworkSnapshotRepositoryFactory.cs | 3 +- .../Snapshots/SnapshotReference.cs | 10 +++ .../SnapshotReferenceTypeConfiguration.cs | 26 +++---- .../DbContexts/GenericDbContext.cs | 11 +-- .../Implementations/Entities/TestEntity.cs | 11 ++- .../Projections/OneToOneProjection.cs | 10 ++- .../Snapshots/ISnapshotWithTestLogic.cs | 6 +- 9 files changed, 104 insertions(+), 47 deletions(-) diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index 5597292a..c1e32ff6 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,7 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Extensions; +using EntityDb.Common.Snapshots; +using EntityDb.EntityFramework.Sessions; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -23,7 +25,7 @@ public static class ServiceCollectionExtensions /// The service collection. /// Modifies the behavior of the repository to accomodate tests. public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) - where TSnapshot : class + where TSnapshot : class, IEntityFrameworkSnapshot where TDbContext : SnapshotReferenceDbContext { serviceCollection.Add> diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index e6cc839b..e1c2cac2 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -1,5 +1,6 @@ using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; +using EntityDb.Common.Snapshots; using EntityDb.EntityFramework.Predicates; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; @@ -11,8 +12,13 @@ namespace EntityDb.EntityFramework.Sessions; +public interface IEntityFrameworkSnapshot : ISnapshot +{ + Expression> GetKeyPredicate(); +} + internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession - where TSnapshot : class + where TSnapshot : class, IEntityFrameworkSnapshot where TDbContext : SnapshotReferenceDbContext { private readonly TDbContext _dbContext; @@ -37,52 +43,88 @@ public async Task Delete(IEnumerable snapshotPointers, CancellationToke { AssertNotReadOnly(); - var set = _dbContext.Set>(); + var snapshotReferenceSet = _dbContext.Set>(); + var snapshotSet = _dbContext.Set(); - var snapshots = await set + var snapshotReferences = await snapshotReferenceSet + .Include(snapshotReference => snapshotReference.Snapshot) .Where(PredicateExpressionBuilder.Or(snapshotPointers, SnapshotPointerPredicate)) - .ToListAsync(cancellationToken); + .ToArrayAsync(cancellationToken); - set.RemoveRange(snapshots); + foreach (var snapshotReference in snapshotReferences) + { + snapshotReferenceSet.Remove(snapshotReference); + + var anyRelatedSnapshotReferences = await snapshotReferenceSet + .Where + ( + relatedSnapshotReference => + relatedSnapshotReference.Id != snapshotReference.Id && + relatedSnapshotReference.SnapshotId == snapshotReference.SnapshotId && + relatedSnapshotReference.SnapshotVersionNumber == snapshotReference.SnapshotVersionNumber + ) + .AnyAsync(cancellationToken); + + if (!anyRelatedSnapshotReferences) + { + snapshotSet.Remove(snapshotReference.Snapshot); + } + } await _dbContext.SaveChangesAsync(cancellationToken); } public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) { - var reference = await _dbContext.Set>() + var snapshotReference = await _dbContext.Set>() + .Include(reference => reference.Snapshot) .AsNoTracking() .Where(SnapshotPointerPredicate(snapshotPointer)) .SingleOrDefaultAsync(cancellationToken); - return reference?.Snapshot; + return snapshotReference?.Snapshot; } public async Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) { AssertNotReadOnly(); - var dbSet = _dbContext.Set>(); + var snapshotSet = _dbContext.Set(); + + var sapshotExists = await snapshotSet + .Where(snapshot.GetKeyPredicate()) + .AnyAsync(cancellationToken); + + if (!sapshotExists) + { + snapshotSet.Add(snapshot); + } + + var snapshotReferenceSet = _dbContext.Set>(); - var previousReference = await dbSet + var previousSnapshotReference = await snapshotReferenceSet .Where(SnapshotPointerPredicate(snapshotPointer)) .SingleOrDefaultAsync(cancellationToken); - if (previousReference != null) + if (previousSnapshotReference != null) { - previousReference.Snapshot = snapshot; + previousSnapshotReference.SnapshotId = snapshot.GetId(); + previousSnapshotReference.SnapshotVersionNumber = snapshot.GetVersionNumber(); + previousSnapshotReference.Snapshot = snapshot; } else { - var reference = new SnapshotReference + var snapshotReference = new SnapshotReference { Id = Guid.NewGuid(), PointerId = snapshotPointer.Id, PointerVersionNumber = snapshotPointer.VersionNumber, - Snapshot = snapshot + SnapshotId = snapshot.GetId(), + SnapshotVersionNumber = snapshot.GetVersionNumber(), + Snapshot = snapshot, }; - dbSet.Add(reference); + snapshotReferenceSet.Add(snapshotReference); } await _dbContext.SaveChangesAsync(cancellationToken); diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index a208be47..e6b8c5de 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -3,14 +3,13 @@ using EntityDb.Common.Snapshots; using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory - where TSnapshot : class + where TSnapshot : class, IEntityFrameworkSnapshot where TDbContext : SnapshotReferenceDbContext { private readonly IServiceProvider _serviceProvider; diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs index b7140d0d..d547183b 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs @@ -23,6 +23,16 @@ public class SnapshotReference /// public required VersionNumber PointerVersionNumber { get; init; } + /// + /// The Id of the Snapshot. + /// + public required Id SnapshotId { get; set; } + + /// + /// The VersionNumber of the Snapshot. + /// + public required VersionNumber SnapshotVersionNumber { get; set; } + /// /// The Snapshot. /// diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs index 56c4443d..4e40aeb5 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -8,17 +8,14 @@ public class SnapshotReferenceTypeConfiguration : IEntityTypeConfigur where TSnapshot : class { private readonly string _snapshotReferencesTableName; - private readonly string _snapshotsTableName; /// /// Configure the napshot Reference Type. /// /// The name of the table for snapshot references. - /// The name of the table for snapshots. - public SnapshotReferenceTypeConfiguration(string snapshotReferencesTableName, string snapshotsTableName) + public SnapshotReferenceTypeConfiguration(string snapshotReferencesTableName) { _snapshotReferencesTableName = snapshotReferencesTableName; - _snapshotsTableName = snapshotsTableName; } /// @@ -36,18 +33,13 @@ public virtual void Configure(EntityTypeBuilder> sn snapshotReference.PointerVersionNumber }); - var snapshotBuilder = snapshotReferenceBuilder - .OwnsOne(snapshotReference => snapshotReference.Snapshot); - - Configure(snapshotBuilder); - } - - /// - /// Configures the snapshot of type . - /// - /// The builder to be used to configure the snapshot type. - protected virtual void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder) - { - snapshotBuilder.ToTable(_snapshotsTableName); + snapshotReferenceBuilder + .HasOne(snapshotReference => snapshotReference.Snapshot) + .WithMany() + .HasForeignKey(snapshotReference => new + { + snapshotReference.SnapshotId, + snapshotReference.SnapshotVersionNumber, + }); } } diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index ee55e0af..1a6dae26 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -29,20 +29,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); + modelBuilder.ApplyConfiguration(new SnapshotTypeConfiguration()); } } public class SnapshotReferenceTypeConfiguration : EntityFramework.Snapshots.SnapshotReferenceTypeConfiguration where TSnapshot : class, ISnapshotWithTestLogic { - public SnapshotReferenceTypeConfiguration() : base($"{typeof(TSnapshot).Name}SnapshotReferences", $"{typeof(TSnapshot).Name}Snapshots") + public SnapshotReferenceTypeConfiguration() : base($"{typeof(TSnapshot).Name}SnapshotReferences") { } +} - protected override void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder) +public class SnapshotTypeConfiguration : IEntityTypeConfiguration + where TSnapshot : class, ISnapshotWithTestLogic +{ + public virtual void Configure(EntityTypeBuilder snapshotBuilder) { - base.Configure(snapshotBuilder); - TSnapshot.Configure(snapshotBuilder); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 02961bf2..759f9337 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,8 +1,8 @@ -using EntityDb.Abstractions.ValueObjects; +using System.Linq.Expressions; +using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Commands; using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace EntityDb.Common.Tests.Implementations.Entities; @@ -20,7 +20,7 @@ public static TestEntity Construct(Id entityId) }; } - public static void Configure(OwnedNavigationBuilder, TestEntity> testEntityBuilder) + public static void Configure(EntityTypeBuilder testEntityBuilder) { testEntityBuilder .HasKey(testEntity => new @@ -70,4 +70,9 @@ public TestEntity WithVersionNumber(VersionNumber versionNumber) public static AsyncLocal?> ShouldRecordLogic { get; } = new(); public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); + + public Expression> GetKeyPredicate() + { + return (testEntity) => testEntity.Id == Id && testEntity.VersionNumber == VersionNumber; + } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 183be609..a3914809 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; using EntityDb.Abstractions.Annotations; using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Reducers; @@ -7,7 +7,6 @@ using EntityDb.Common.Queries; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; @@ -27,7 +26,7 @@ public static OneToOneProjection Construct(Id projectionId) }; } - public static void Configure(OwnedNavigationBuilder, OneToOneProjection> oneToOneProjectionBuilder) + public static void Configure(EntityTypeBuilder oneToOneProjectionBuilder) { oneToOneProjectionBuilder .HasKey(oneToOneProjection => new @@ -93,4 +92,9 @@ public OneToOneProjection WithVersionNumber(VersionNumber versionNumber) public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); + + public Expression> GetKeyPredicate() + { + return (oneToOneProjection) => oneToOneProjection.Id == Id && oneToOneProjection.VersionNumber == VersionNumber; + } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs index b74ee380..b86a3e79 100644 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs @@ -1,17 +1,17 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Snapshots; -using EntityDb.EntityFramework.Snapshots; +using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace EntityDb.Common.Tests.Implementations.Snapshots; -public interface ISnapshotWithTestLogic : ISnapshot +public interface ISnapshotWithTestLogic : ISnapshot, IEntityFrameworkSnapshot where TSnapshot : class { VersionNumber VersionNumber { get; } static abstract string RedisKeyNamespace { get; } static abstract AsyncLocal?> ShouldRecordLogic { get; } static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } - static abstract void Configure(OwnedNavigationBuilder, TSnapshot> snapshotBuilder); + static abstract void Configure(EntityTypeBuilder snapshotBuilder); TSnapshot WithVersionNumber(VersionNumber versionNumber); } \ No newline at end of file From cfcb72176c23e96d7573248978314c5fe162b1f7 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Fri, 25 Aug 2023 17:10:17 -0700 Subject: [PATCH 31/93] nit: explicit OnDelete behavior --- .../Snapshots/SnapshotReferenceTypeConfiguration.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs index 4e40aeb5..833b58b2 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -40,6 +40,7 @@ public virtual void Configure(EntityTypeBuilder> sn { snapshotReference.SnapshotId, snapshotReference.SnapshotVersionNumber, - }); + }) + .OnDelete(DeleteBehavior.Cascade); } } From 5b6a94a0bd2ef4246660bdb62c26519018a86a02 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Aug 2023 00:08:41 -0700 Subject: [PATCH 32/93] fix: IEntityFrameworkSnapshot belongs in its own file, and in the Snapshots namespace --- .../Sessions/EntityFrameworkSession.cs | 5 ----- .../Snapshots/IEntityFrameworkSnapshot.cs | 17 +++++++++++++++++ .../Snapshots/ISnapshotWithTestLogic.cs | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index e1c2cac2..2363a06f 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -12,11 +12,6 @@ namespace EntityDb.EntityFramework.Sessions; -public interface IEntityFrameworkSnapshot : ISnapshot -{ - Expression> GetKeyPredicate(); -} - internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession where TSnapshot : class, IEntityFrameworkSnapshot where TDbContext : SnapshotReferenceDbContext diff --git a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs new file mode 100644 index 00000000..65a76736 --- /dev/null +++ b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs @@ -0,0 +1,17 @@ +using EntityDb.Common.Snapshots; +using System.Linq.Expressions; + +namespace EntityDb.EntityFramework.Snapshots; + +/// +/// Indicates that a snapshot is compatible with EntityDb.EntityFramework implementations. +/// +/// The type of the snapshot +public interface IEntityFrameworkSnapshot : ISnapshot +{ + /// + /// Returns an expression of a function that can be used to check if a copy of this record already exists. + /// + /// An expression of a function that can be used to check if a copy of this record already exists. + Expression> GetKeyPredicate(); +} diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs index b86a3e79..72b2a309 100644 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Snapshots; -using EntityDb.EntityFramework.Sessions; +using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace EntityDb.Common.Tests.Implementations.Snapshots; From 8ae39136169d311c1b939abc5654a786c338a0bd Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Aug 2023 13:54:08 -0700 Subject: [PATCH 33/93] refactor: better snapshot cleanup 1. delete snapshot when all references deleted 2. delete snapshot when all references are changed to point to a different snapshot 3. provide an option to opt-out of cleanup behavior --- .../Sessions/EntityFrameworkSession.cs | 72 +++++++++++-------- .../EntityFrameworkSnapshotSessionOptions.cs | 10 +++ 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index 2363a06f..a61c0607 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -1,6 +1,5 @@ using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; -using EntityDb.Common.Snapshots; using EntityDb.EntityFramework.Predicates; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; @@ -17,6 +16,8 @@ internal class EntityFrameworkSession : DisposableResourc where TDbContext : SnapshotReferenceDbContext { private readonly TDbContext _dbContext; + private readonly DbSet> _snapshotReferences; + private readonly DbSet _snapshots; private readonly EntityFrameworkSnapshotSessionOptions _options; private IDbContextTransaction? Transaction { get; set; } @@ -24,6 +25,8 @@ internal class EntityFrameworkSession : DisposableResourc public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessionOptions options) { _dbContext = dbContext; + _snapshotReferences = dbContext.Set>(); + _snapshots = dbContext.Set(); _options = options; } @@ -34,36 +37,43 @@ private static Expression, bool>> SnapshotPoin snapshotReference.PointerVersionNumber == snapshotPointer.VersionNumber; } + private async Task ShouldDeleteSnapshot(SnapshotReference snapshotReference, CancellationToken cancellationToken) + { + if (_options.KeepSnapshotsWithoutSnapshotReferences) + { + return false; + } + + var otherSnapshotReferences = await _snapshotReferences + .Where + ( + relatedSnapshotReference => + relatedSnapshotReference.Id != snapshotReference.Id && + relatedSnapshotReference.SnapshotId == snapshotReference.SnapshotId && + relatedSnapshotReference.SnapshotVersionNumber == snapshotReference.SnapshotVersionNumber + ) + .AnyAsync(cancellationToken); + + return !otherSnapshotReferences; + } + public async Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken) { AssertNotReadOnly(); - var snapshotReferenceSet = _dbContext.Set>(); - var snapshotSet = _dbContext.Set(); - - var snapshotReferences = await snapshotReferenceSet + var snapshotReferences = await _snapshotReferences .Include(snapshotReference => snapshotReference.Snapshot) .Where(PredicateExpressionBuilder.Or(snapshotPointers, SnapshotPointerPredicate)) .ToArrayAsync(cancellationToken); foreach (var snapshotReference in snapshotReferences) { - snapshotReferenceSet.Remove(snapshotReference); - - var anyRelatedSnapshotReferences = await snapshotReferenceSet - .Where - ( - relatedSnapshotReference => - relatedSnapshotReference.Id != snapshotReference.Id && - relatedSnapshotReference.SnapshotId == snapshotReference.SnapshotId && - relatedSnapshotReference.SnapshotVersionNumber == snapshotReference.SnapshotVersionNumber - ) - .AnyAsync(cancellationToken); - - if (!anyRelatedSnapshotReferences) + if (await ShouldDeleteSnapshot(snapshotReference, cancellationToken)) { - snapshotSet.Remove(snapshotReference.Snapshot); + _snapshots.Remove(snapshotReference.Snapshot); } + + _snapshotReferences.Remove(snapshotReference); } await _dbContext.SaveChangesAsync(cancellationToken); @@ -71,7 +81,7 @@ public async Task Delete(IEnumerable snapshotPointers, CancellationToke public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) { - var snapshotReference = await _dbContext.Set>() + var snapshotReference = await _snapshotReferences .Include(reference => reference.Snapshot) .AsNoTracking() .Where(SnapshotPointerPredicate(snapshotPointer)) @@ -84,32 +94,34 @@ public async Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, Cancellati { AssertNotReadOnly(); - var snapshotSet = _dbContext.Set(); - - var sapshotExists = await snapshotSet + var sapshotExists = await _snapshots .Where(snapshot.GetKeyPredicate()) .AnyAsync(cancellationToken); if (!sapshotExists) { - snapshotSet.Add(snapshot); + _snapshots.Add(snapshot); } - var snapshotReferenceSet = _dbContext.Set>(); - - var previousSnapshotReference = await snapshotReferenceSet + var previousSnapshotReference = await _snapshotReferences + .Include(snapshotReference => snapshotReference.Snapshot) .Where(SnapshotPointerPredicate(snapshotPointer)) .SingleOrDefaultAsync(cancellationToken); if (previousSnapshotReference != null) { + if (await ShouldDeleteSnapshot(previousSnapshotReference, cancellationToken)) + { + _snapshots.Remove(previousSnapshotReference.Snapshot); + } + previousSnapshotReference.SnapshotId = snapshot.GetId(); previousSnapshotReference.SnapshotVersionNumber = snapshot.GetVersionNumber(); previousSnapshotReference.Snapshot = snapshot; } else { - var snapshotReference = new SnapshotReference + _snapshotReferences.Add(new SnapshotReference { Id = Guid.NewGuid(), PointerId = snapshotPointer.Id, @@ -117,9 +129,7 @@ public async Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, Cancellati SnapshotId = snapshot.GetId(), SnapshotVersionNumber = snapshot.GetVersionNumber(), Snapshot = snapshot, - }; - - snapshotReferenceSet.Add(snapshotReference); + }); } await _dbContext.SaveChangesAsync(cancellationToken); diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 1ed25e2f..2d69a65b 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Snapshots; +using EntityDb.EntityFramework.Snapshots; using System.Diagnostics.CodeAnalysis; namespace EntityDb.EntityFramework.Sessions; @@ -13,6 +14,15 @@ public class EntityFrameworkSnapshotSessionOptions /// public bool ReadOnly { get; set; } + /// + /// If false, a snapshot will be deleted if there are no + /// records pointing to the snapshot record. + /// + /// + /// You may consider setting this to true if there are other records which reference a specific snapshot. + /// + public bool KeepSnapshotsWithoutSnapshotReferences { get; set; } + /// [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() From 8a275c7907a8527dfbf92f1a861edb6cd24f373b Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Aug 2023 16:39:56 -0700 Subject: [PATCH 34/93] refactor: force a constructor instead of using IDbContextFactory this allows for a single DbContext to have multiple connection configurations (e.g., read-only connection string) --- .../BufferBlockTransactionReprocessorQueue.cs | 6 ---- .../Extensions/ServiceCollectionExtensions.cs | 6 ++-- ...bTransactionRepositoryFactoryExtensions.cs | 4 +-- .../Sessions/EntityFrameworkSession.cs | 8 ++++- .../EntityFrameworkSnapshotSessionOptions.cs | 2 +- .../Sessions/IEntityFrameworkSession.cs | 3 ++ .../TestModeEntityFrameworkSession.cs | 3 ++ ...ntityFrameworkSnapshotRepositoryFactory.cs | 9 ++--- ...ntityFrameworkSnapshotRepositoryFactory.cs | 4 +-- .../Snapshots/SnapshotReferenceDbContext.cs | 11 +++--- ...ntityFrameworkSnapshotRepositoryFactory.cs | 36 ++++++++++++++++--- .../DbContexts/GenericDbContext.cs | 32 +++++++++-------- test/EntityDb.Common.Tests/TestsBase.cs | 9 ++--- 13 files changed, 83 insertions(+), 50 deletions(-) diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs index 85936b28..98e1221c 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs +++ b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs @@ -11,15 +11,9 @@ namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; internal class BufferBlockTransactionReprocessorQueue : TransactionReprocessorQueueBase, ITransactionReprocessorQueue { private readonly BufferBlock _bufferBlock = new(); - private readonly ILogger _logger; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; - private readonly IEnumerable _transactionProcessors; public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) : base(logger, transactionRepositoryFactory, transactionProcessors) { - _logger = logger; - _transactionRepositoryFactory = transactionRepositoryFactory; - _transactionProcessors = transactionProcessors; } public void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest) diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index c1e32ff6..9676bd40 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -1,7 +1,5 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Extensions; -using EntityDb.Common.Snapshots; -using EntityDb.EntityFramework.Sessions; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -26,7 +24,7 @@ public static class ServiceCollectionExtensions /// Modifies the behavior of the repository to accomodate tests. public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : SnapshotReferenceDbContext + where TDbContext : DbContext, ISnapshotReferenceDbContext { serviceCollection.Add> ( @@ -38,7 +36,7 @@ public static void AddEntityFrameworkSnapshots(this IServ testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, serviceProvider => serviceProvider .GetRequiredService>() - .UseTestMode(testMode) + .UseTestMode(serviceProvider, testMode) ); } } diff --git a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs index a65d1aea..629e685b 100644 --- a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs @@ -5,14 +5,14 @@ namespace EntityDb.EntityFramework.Extensions; internal static class EntityFrameworkSnapshotRepositoryFactoryExtensions { - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] public static IEntityFrameworkSnapshotRepositoryFactory UseTestMode( this IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoyFactory, + IServiceProvider serviceProvider, bool testMode) { return testMode - ? new TestModeEntityFrameworkSnapshotRepositoryFactory(entityFrameworkSnapshotRepositoyFactory) + ? TestModeEntityFrameworkSnapshotRepositoryFactory.Create(serviceProvider, entityFrameworkSnapshotRepositoyFactory) : entityFrameworkSnapshotRepositoyFactory; } } diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index a61c0607..c5d64f07 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -13,13 +13,14 @@ namespace EntityDb.EntityFramework.Sessions; internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : SnapshotReferenceDbContext + where TDbContext : DbContext { private readonly TDbContext _dbContext; private readonly DbSet> _snapshotReferences; private readonly DbSet _snapshots; private readonly EntityFrameworkSnapshotSessionOptions _options; + DbContext IEntityFrameworkSession.DbContext => _dbContext; private IDbContextTransaction? Transaction { get; set; } public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessionOptions options) @@ -186,4 +187,9 @@ public IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrame { return new EntityFrameworkSession(_dbContext, snapshotSessionOptions); } + + public override ValueTask DisposeAsync() + { + return _dbContext.DisposeAsync(); + } } diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 966342ae..2d28768c 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -5,7 +5,7 @@ namespace EntityDb.EntityFramework.Sessions; /// -/// Configuration options for the Redis implementation of . +/// Configuration options for the EntityFramework implementation of . /// public class EntityFrameworkSnapshotSessionOptions { diff --git a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs index c1a89863..d81515c0 100644 --- a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs @@ -1,10 +1,13 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.ValueObjects; +using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Sessions; internal interface IEntityFrameworkSession : IDisposableResource { + internal DbContext DbContext { get; } + IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); Task StartTransaction(CancellationToken cancellationToken); diff --git a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs index de0d1ba9..fc750f83 100644 --- a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs @@ -1,10 +1,13 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; +using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Sessions; internal record TestModeEntityFrameworkSession(IEntityFrameworkSession EntityFrameworkSession) : DisposableResourceBaseRecord, IEntityFrameworkSession { + DbContext IEntityFrameworkSession.DbContext => EntityFrameworkSession.DbContext; + public Task StartTransaction(CancellationToken cancellationToken) { // Test Mode Transactions are started in the Test Mode Repository Factory diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index e6b8c5de..9bf2b630 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -10,21 +10,18 @@ namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : SnapshotReferenceDbContext + where TDbContext : DbContext, ISnapshotReferenceDbContext { private readonly IServiceProvider _serviceProvider; - private readonly IDbContextFactory _dbContextFactory; private readonly IOptionsFactory _optionsFactory; public EntityFrameworkSnapshotRepositoryFactory ( IServiceProvider serviceProvider, - IDbContextFactory dbContextFactory, IOptionsFactory optionsFactory ) { _serviceProvider = serviceProvider; - _dbContextFactory = dbContextFactory; _optionsFactory = optionsFactory; } @@ -40,12 +37,12 @@ public ISnapshotRepository CreateRepository(IEntityFrameworkSession> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken) { - var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); + var dbContext = await TDbContext.ConstructAsync(options); return EntityFrameworkSession.Create(_serviceProvider, dbContext, options); } - public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) + public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) { return _optionsFactory.Create(snapshotSessionOptionsName); } diff --git a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs index 6cde2cc5..9777116e 100644 --- a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs @@ -8,14 +8,14 @@ internal interface IEntityFrameworkSnapshotRepositoryFactory : ISnaps async Task> ISnapshotRepositoryFactory.CreateRepository( string snapshotSessionOptionsName, CancellationToken cancellationToken) { - var options = GetTransactionSessionOptions(snapshotSessionOptionsName); + var options = GetSessionOptions(snapshotSessionOptionsName); var entityFrameworkSession = await CreateSession(options, cancellationToken); return CreateRepository(entityFrameworkSession); } - EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName); + EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName); Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken); diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs index 2f8f5864..0431f3b7 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs @@ -1,19 +1,20 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.EntityFramework.Converters; +using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Snapshots; +public interface ISnapshotReferenceDbContext where TDbContext : ISnapshotReferenceDbContext +{ + static abstract Task ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); +} + /// /// A DbContext that adds basic converters for types defined in /// public class SnapshotReferenceDbContext : DbContext { - /// - public SnapshotReferenceDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - /// protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { diff --git a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs index e6deb576..f29ca43b 100644 --- a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs @@ -1,18 +1,27 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Disposables; using EntityDb.EntityFramework.Sessions; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace EntityDb.EntityFramework.Snapshots; internal class TestModeEntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory { + private readonly ILogger> _logger; private readonly IEntityFrameworkSnapshotRepositoryFactory _entityFrameworkSnapshotRepositoryFactory; private (IEntityFrameworkSession Normal, TestModeEntityFrameworkSession TestMode)? _sessions; - public TestModeEntityFrameworkSnapshotRepositoryFactory( - IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory) + public TestModeEntityFrameworkSnapshotRepositoryFactory + ( + ILogger> logger, + IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory + ) { + _logger = logger; _entityFrameworkSnapshotRepositoryFactory = entityFrameworkSnapshotRepositoryFactory; } @@ -32,11 +41,23 @@ public async Task> CreateSession(EntityFramew var normalOptions = new EntityFrameworkSnapshotSessionOptions { - ReadOnly = false + ConnectionString = options.ConnectionString, + KeepSnapshotsWithoutSnapshotReferences = options.KeepSnapshotsWithoutSnapshotReferences, }; var normalSession = await _entityFrameworkSnapshotRepositoryFactory.CreateSession(normalOptions, cancellationToken); + try + { + var databaseCreator = (RelationalDatabaseCreator)normalSession.DbContext.Database.GetService(); + + await databaseCreator.CreateTablesAsync(cancellationToken); + } + catch (Exception exception) + { + _logger.LogDebug(exception, "It looks like the database tables have already been created"); + } + var testModeSession = new TestModeEntityFrameworkSession(normalSession); await normalSession.StartTransaction(default); @@ -56,8 +77,13 @@ public override async ValueTask DisposeAsync() } } - public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) + public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) + { + return _entityFrameworkSnapshotRepositoryFactory.GetSessionOptions(snapshotSessionOptionsName); + } + + public static TestModeEntityFrameworkSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory) { - return _entityFrameworkSnapshotRepositoryFactory.GetTransactionSessionOptions(snapshotSessionOptionsName); + return ActivatorUtilities.CreateInstance>(serviceProvider, entityFrameworkSnapshotRepositoryFactory); } } diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 1a6dae26..82632222 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -1,27 +1,31 @@ using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.Sessions; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.EntityFrameworkCore.Storage; namespace EntityDb.Common.Tests.Implementations.DbContexts; -internal class GenericDbContext : SnapshotReferenceDbContext +internal class GenericDbContext : SnapshotReferenceDbContext, ISnapshotReferenceDbContext> where TSnapshot : class, ISnapshotWithTestLogic { - public GenericDbContext(DbContextOptions> options) : base(options) + private readonly EntityFrameworkSnapshotSessionOptions _options; + + public GenericDbContext(EntityFrameworkSnapshotSessionOptions options) + { + _options = options; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .UseNpgsql($"{_options.ConnectionString};Include Error Detail=true") + .EnableSensitiveDataLogging(); + } + + public static Task> ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) { - try - { - var databaseCreator = (RelationalDatabaseCreator)Database.GetService(); - - databaseCreator.CreateTables(); - } - catch - { - // One of the tests has already created the tables. - } + return Task.FromResult(new GenericDbContext(entityFrameworkSnapshotSessionOptions)); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 3850d235..0cd3f810 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -163,12 +163,13 @@ private static SnapshotAdder EntityFrameworkSnapshotAdder() .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) .ImplementationInstance as DatabaseContainerFixture; - serviceCollection.AddDbContextFactory>(options => options - .UseNpgsql($"{databaseContainerFixture!.PostgreSqlContainer.ConnectionString};Include Error Detail=true") - .EnableSensitiveDataLogging()); - serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); + serviceCollection.ConfigureAll(options => + { + options.ConnectionString = databaseContainerFixture!.PostgreSqlContainer.ConnectionString; + }); + serviceCollection.Configure(TestSessionOptions.Write, options => { options.ReadOnly = false; From 218c0e512387c21d8ad7d660bdc5643f336c7856 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Aug 2023 16:39:56 -0700 Subject: [PATCH 35/93] refactor: force a constructor instead of using IDbContextFactory this allows for a single DbContext to have multiple connection configurations (e.g., read-only connection string) --- .../Extensions/ServiceCollectionExtensions.cs | 6 ++-- ...bTransactionRepositoryFactoryExtensions.cs | 4 +-- .../Sessions/EntityFrameworkSession.cs | 8 ++++- .../EntityFrameworkSnapshotSessionOptions.cs | 2 +- .../Sessions/IEntityFrameworkSession.cs | 3 ++ .../TestModeEntityFrameworkSession.cs | 3 ++ ...ntityFrameworkSnapshotRepositoryFactory.cs | 9 ++--- ...ntityFrameworkSnapshotRepositoryFactory.cs | 4 +-- .../Snapshots/SnapshotReferenceDbContext.cs | 11 +++--- ...ntityFrameworkSnapshotRepositoryFactory.cs | 36 ++++++++++++++++--- .../DbContexts/GenericDbContext.cs | 32 +++++++++-------- test/EntityDb.Common.Tests/TestsBase.cs | 9 ++--- 12 files changed, 83 insertions(+), 44 deletions(-) diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index c1e32ff6..9676bd40 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -1,7 +1,5 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Extensions; -using EntityDb.Common.Snapshots; -using EntityDb.EntityFramework.Sessions; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -26,7 +24,7 @@ public static class ServiceCollectionExtensions /// Modifies the behavior of the repository to accomodate tests. public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : SnapshotReferenceDbContext + where TDbContext : DbContext, ISnapshotReferenceDbContext { serviceCollection.Add> ( @@ -38,7 +36,7 @@ public static void AddEntityFrameworkSnapshots(this IServ testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, serviceProvider => serviceProvider .GetRequiredService>() - .UseTestMode(testMode) + .UseTestMode(serviceProvider, testMode) ); } } diff --git a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs index a65d1aea..629e685b 100644 --- a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs @@ -5,14 +5,14 @@ namespace EntityDb.EntityFramework.Extensions; internal static class EntityFrameworkSnapshotRepositoryFactoryExtensions { - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] public static IEntityFrameworkSnapshotRepositoryFactory UseTestMode( this IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoyFactory, + IServiceProvider serviceProvider, bool testMode) { return testMode - ? new TestModeEntityFrameworkSnapshotRepositoryFactory(entityFrameworkSnapshotRepositoyFactory) + ? TestModeEntityFrameworkSnapshotRepositoryFactory.Create(serviceProvider, entityFrameworkSnapshotRepositoyFactory) : entityFrameworkSnapshotRepositoyFactory; } } diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index a61c0607..c5d64f07 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -13,13 +13,14 @@ namespace EntityDb.EntityFramework.Sessions; internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : SnapshotReferenceDbContext + where TDbContext : DbContext { private readonly TDbContext _dbContext; private readonly DbSet> _snapshotReferences; private readonly DbSet _snapshots; private readonly EntityFrameworkSnapshotSessionOptions _options; + DbContext IEntityFrameworkSession.DbContext => _dbContext; private IDbContextTransaction? Transaction { get; set; } public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessionOptions options) @@ -186,4 +187,9 @@ public IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrame { return new EntityFrameworkSession(_dbContext, snapshotSessionOptions); } + + public override ValueTask DisposeAsync() + { + return _dbContext.DisposeAsync(); + } } diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 2d69a65b..5494ed0b 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -5,7 +5,7 @@ namespace EntityDb.EntityFramework.Sessions; /// -/// Configuration options for the Redis implementation of . +/// Configuration options for the EntityFramework implementation of . /// public class EntityFrameworkSnapshotSessionOptions { diff --git a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs index c1a89863..d81515c0 100644 --- a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs @@ -1,10 +1,13 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.ValueObjects; +using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Sessions; internal interface IEntityFrameworkSession : IDisposableResource { + internal DbContext DbContext { get; } + IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); Task StartTransaction(CancellationToken cancellationToken); diff --git a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs index de0d1ba9..fc750f83 100644 --- a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs @@ -1,10 +1,13 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; +using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Sessions; internal record TestModeEntityFrameworkSession(IEntityFrameworkSession EntityFrameworkSession) : DisposableResourceBaseRecord, IEntityFrameworkSession { + DbContext IEntityFrameworkSession.DbContext => EntityFrameworkSession.DbContext; + public Task StartTransaction(CancellationToken cancellationToken) { // Test Mode Transactions are started in the Test Mode Repository Factory diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index e6b8c5de..9bf2b630 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -10,21 +10,18 @@ namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : SnapshotReferenceDbContext + where TDbContext : DbContext, ISnapshotReferenceDbContext { private readonly IServiceProvider _serviceProvider; - private readonly IDbContextFactory _dbContextFactory; private readonly IOptionsFactory _optionsFactory; public EntityFrameworkSnapshotRepositoryFactory ( IServiceProvider serviceProvider, - IDbContextFactory dbContextFactory, IOptionsFactory optionsFactory ) { _serviceProvider = serviceProvider; - _dbContextFactory = dbContextFactory; _optionsFactory = optionsFactory; } @@ -40,12 +37,12 @@ public ISnapshotRepository CreateRepository(IEntityFrameworkSession> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken) { - var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); + var dbContext = await TDbContext.ConstructAsync(options); return EntityFrameworkSession.Create(_serviceProvider, dbContext, options); } - public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) + public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) { return _optionsFactory.Create(snapshotSessionOptionsName); } diff --git a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs index 6cde2cc5..9777116e 100644 --- a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs @@ -8,14 +8,14 @@ internal interface IEntityFrameworkSnapshotRepositoryFactory : ISnaps async Task> ISnapshotRepositoryFactory.CreateRepository( string snapshotSessionOptionsName, CancellationToken cancellationToken) { - var options = GetTransactionSessionOptions(snapshotSessionOptionsName); + var options = GetSessionOptions(snapshotSessionOptionsName); var entityFrameworkSession = await CreateSession(options, cancellationToken); return CreateRepository(entityFrameworkSession); } - EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName); + EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName); Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken); diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs index 2f8f5864..0431f3b7 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs @@ -1,19 +1,20 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.EntityFramework.Converters; +using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.Snapshots; +public interface ISnapshotReferenceDbContext where TDbContext : ISnapshotReferenceDbContext +{ + static abstract Task ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); +} + /// /// A DbContext that adds basic converters for types defined in /// public class SnapshotReferenceDbContext : DbContext { - /// - public SnapshotReferenceDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - /// protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { diff --git a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs index e6deb576..f29ca43b 100644 --- a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs @@ -1,18 +1,27 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Disposables; using EntityDb.EntityFramework.Sessions; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace EntityDb.EntityFramework.Snapshots; internal class TestModeEntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory { + private readonly ILogger> _logger; private readonly IEntityFrameworkSnapshotRepositoryFactory _entityFrameworkSnapshotRepositoryFactory; private (IEntityFrameworkSession Normal, TestModeEntityFrameworkSession TestMode)? _sessions; - public TestModeEntityFrameworkSnapshotRepositoryFactory( - IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory) + public TestModeEntityFrameworkSnapshotRepositoryFactory + ( + ILogger> logger, + IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory + ) { + _logger = logger; _entityFrameworkSnapshotRepositoryFactory = entityFrameworkSnapshotRepositoryFactory; } @@ -32,11 +41,23 @@ public async Task> CreateSession(EntityFramew var normalOptions = new EntityFrameworkSnapshotSessionOptions { - ReadOnly = false + ConnectionString = options.ConnectionString, + KeepSnapshotsWithoutSnapshotReferences = options.KeepSnapshotsWithoutSnapshotReferences, }; var normalSession = await _entityFrameworkSnapshotRepositoryFactory.CreateSession(normalOptions, cancellationToken); + try + { + var databaseCreator = (RelationalDatabaseCreator)normalSession.DbContext.Database.GetService(); + + await databaseCreator.CreateTablesAsync(cancellationToken); + } + catch (Exception exception) + { + _logger.LogDebug(exception, "It looks like the database tables have already been created"); + } + var testModeSession = new TestModeEntityFrameworkSession(normalSession); await normalSession.StartTransaction(default); @@ -56,8 +77,13 @@ public override async ValueTask DisposeAsync() } } - public EntityFrameworkSnapshotSessionOptions GetTransactionSessionOptions(string snapshotSessionOptionsName) + public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) + { + return _entityFrameworkSnapshotRepositoryFactory.GetSessionOptions(snapshotSessionOptionsName); + } + + public static TestModeEntityFrameworkSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory) { - return _entityFrameworkSnapshotRepositoryFactory.GetTransactionSessionOptions(snapshotSessionOptionsName); + return ActivatorUtilities.CreateInstance>(serviceProvider, entityFrameworkSnapshotRepositoryFactory); } } diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 1a6dae26..82632222 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -1,27 +1,31 @@ using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.Sessions; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.EntityFrameworkCore.Storage; namespace EntityDb.Common.Tests.Implementations.DbContexts; -internal class GenericDbContext : SnapshotReferenceDbContext +internal class GenericDbContext : SnapshotReferenceDbContext, ISnapshotReferenceDbContext> where TSnapshot : class, ISnapshotWithTestLogic { - public GenericDbContext(DbContextOptions> options) : base(options) + private readonly EntityFrameworkSnapshotSessionOptions _options; + + public GenericDbContext(EntityFrameworkSnapshotSessionOptions options) + { + _options = options; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .UseNpgsql($"{_options.ConnectionString};Include Error Detail=true") + .EnableSensitiveDataLogging(); + } + + public static Task> ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) { - try - { - var databaseCreator = (RelationalDatabaseCreator)Database.GetService(); - - databaseCreator.CreateTables(); - } - catch - { - // One of the tests has already created the tables. - } + return Task.FromResult(new GenericDbContext(entityFrameworkSnapshotSessionOptions)); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 57cd6e2f..ef397161 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -163,12 +163,13 @@ private static SnapshotAdder EntityFrameworkSnapshotAdder() .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) .ImplementationInstance as DatabaseContainerFixture; - serviceCollection.AddDbContextFactory>(options => options - .UseNpgsql($"{databaseContainerFixture!.PostgreSqlContainer.ConnectionString};Include Error Detail=true") - .EnableSensitiveDataLogging()); - serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); + serviceCollection.ConfigureAll(options => + { + options.ConnectionString = databaseContainerFixture!.PostgreSqlContainer.ConnectionString; + }); + serviceCollection.Configure(TestSessionOptions.Write, options => { options.ReadOnly = false; From 4a2251e9384002e5f1c2d8a01bf29dfa095f56ec Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Aug 2023 16:58:00 -0700 Subject: [PATCH 36/93] fix: somehow this wasn't on this branch originally --- .../Sessions/EntityFrameworkSnapshotSessionOptions.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 5494ed0b..2d28768c 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -9,6 +9,13 @@ namespace EntityDb.EntityFramework.Sessions; /// public class EntityFrameworkSnapshotSessionOptions { + /// + /// This property is not used by the package. It only provides a convenient way to access + /// the connection string using IOptions, which does not appear to be a convienent thing + /// to do in vanilla Enitity Framework. + /// + public string ConnectionString { get; set; } = default!; + /// /// If true, indicates the agent only intends to execute queries. /// From 272d6fe9b918750dd873b40d0c8e95fcf0eb2293 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 26 Aug 2023 17:02:38 -0700 Subject: [PATCH 37/93] refactor: rename bits and bobs IEntityDbContext, EntityDbContextBase does not explicitly depend on involving snapshots --- .../EntityDbContextBase.cs} | 12 +++--------- .../DbContexts/IEntityDbContext.cs | 19 +++++++++++++++++++ .../Extensions/ServiceCollectionExtensions.cs | 3 ++- ...ntityFrameworkSnapshotRepositoryFactory.cs | 3 ++- .../DbContexts/GenericDbContext.cs | 4 ++-- 5 files changed, 28 insertions(+), 13 deletions(-) rename src/EntityDb.EntityFramework/{Snapshots/SnapshotReferenceDbContext.cs => DbContexts/EntityDbContextBase.cs} (61%) create mode 100644 src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs similarity index 61% rename from src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs rename to src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs index 0431f3b7..3fb75b6f 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceDbContext.cs +++ b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs @@ -1,19 +1,13 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.EntityFramework.Converters; -using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; -namespace EntityDb.EntityFramework.Snapshots; - -public interface ISnapshotReferenceDbContext where TDbContext : ISnapshotReferenceDbContext -{ - static abstract Task ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); -} +namespace EntityDb.EntityFramework.DbContexts; /// -/// A DbContext that adds basic converters for types defined in +/// A DbContext that adds basic converters for types defined in /// -public class SnapshotReferenceDbContext : DbContext +public abstract class EntityDbContextBase : DbContext { /// protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs new file mode 100644 index 00000000..cc14825f --- /dev/null +++ b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs @@ -0,0 +1,19 @@ +using EntityDb.EntityFramework.Sessions; +using Microsoft.EntityFrameworkCore; + +namespace EntityDb.EntityFramework.DbContexts; + +/// +/// A type of a that can be used for EntityDb purposes. +/// +/// The type of the +public interface IEntityDbContext + where TDbContext : DbContext, IEntityDbContext +{ + /// + /// Returns a new that will be configured using . + /// + /// The options for the database + /// A new that will be configured using . + static abstract Task ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); +} diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index 9676bd40..f379f049 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Extensions; +using EntityDb.EntityFramework.DbContexts; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -24,7 +25,7 @@ public static class ServiceCollectionExtensions /// Modifies the behavior of the repository to accomodate tests. public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : DbContext, ISnapshotReferenceDbContext + where TDbContext : DbContext, IEntityDbContext { serviceCollection.Add> ( diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index 9bf2b630..23ef9a8b 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -1,6 +1,7 @@ using EntityDb.Abstractions.Snapshots; using EntityDb.Common.Disposables; using EntityDb.Common.Snapshots; +using EntityDb.EntityFramework.DbContexts; using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; @@ -10,7 +11,7 @@ namespace EntityDb.EntityFramework.Snapshots; internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : DbContext, ISnapshotReferenceDbContext + where TDbContext : DbContext, IEntityDbContext { private readonly IServiceProvider _serviceProvider; private readonly IOptionsFactory _optionsFactory; diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 82632222..60f3255a 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -1,12 +1,12 @@ using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.EntityFramework.DbContexts; using EntityDb.EntityFramework.Sessions; -using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace EntityDb.Common.Tests.Implementations.DbContexts; -internal class GenericDbContext : SnapshotReferenceDbContext, ISnapshotReferenceDbContext> +internal class GenericDbContext : EntityDbContextBase, IEntityDbContext> where TSnapshot : class, ISnapshotWithTestLogic { private readonly EntityFrameworkSnapshotSessionOptions _options; From 91dda92527597963c383ad6a277c3faa1be9012a Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 27 Aug 2023 08:17:36 -0700 Subject: [PATCH 38/93] refactor: IEntityDbContextFactory allow the end user to create DbContexts using just session option names --- .../DbContexts/EntityDbContextFactory.cs | 26 +++++++++++++++++++ .../DbContexts/IEntityDbContext.cs | 2 +- .../DbContexts/IEntityDbContextFactory.cs | 20 ++++++++++++++ .../Extensions/ServiceCollectionExtensions.cs | 5 ++++ ...ntityFrameworkSnapshotRepositoryFactory.cs | 11 +++++--- .../DbContexts/GenericDbContext.cs | 4 +-- 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs create mode 100644 src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs diff --git a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs new file mode 100644 index 00000000..ca8d5471 --- /dev/null +++ b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs @@ -0,0 +1,26 @@ +using EntityDb.EntityFramework.Sessions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace EntityDb.EntityFramework.DbContexts; + +internal class EntityDbContextFactory : IEntityDbContextFactory + where TDbContext : DbContext, IEntityDbContext +{ + private readonly IOptionsFactory _optionsFactory; + + public EntityDbContextFactory(IOptionsFactory optionsFactory) + { + _optionsFactory = optionsFactory; + } + + public TDbContext Create(string snapshotSessionOptionsName) + { + return TDbContext.Construct(_optionsFactory.Create(snapshotSessionOptionsName)); + } + + TDbContext IEntityDbContextFactory.Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) + { + return TDbContext.Construct(snapshotSessionOptions); + } +} diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs index cc14825f..4661b7a2 100644 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs +++ b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs @@ -15,5 +15,5 @@ public interface IEntityDbContext /// /// The options for the database /// A new that will be configured using . - static abstract Task ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); + static abstract TDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); } diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs new file mode 100644 index 00000000..4fba5f01 --- /dev/null +++ b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs @@ -0,0 +1,20 @@ +using EntityDb.EntityFramework.Sessions; +using Microsoft.EntityFrameworkCore; + +namespace EntityDb.EntityFramework.DbContexts; + +/// +/// Represents a type used to create instances of . +/// +/// The type of the . +public interface IEntityDbContextFactory +{ + internal TDbContext Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); + + /// + /// Create a new instance of . + /// + /// The agent's use case for the . + /// A new instance of . + TDbContext Create(string snapshotSessionOptionsName); +} diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index f379f049..b5ff49a8 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -27,6 +27,11 @@ public static void AddEntityFrameworkSnapshots(this IServ where TSnapshot : class, IEntityFrameworkSnapshot where TDbContext : DbContext, IEntityDbContext { + serviceCollection.Add, EntityDbContextFactory> + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient + ); + serviceCollection.Add> ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs index 23ef9a8b..4c0c1254 100644 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs @@ -15,15 +15,18 @@ internal class EntityFrameworkSnapshotRepositoryFactory : { private readonly IServiceProvider _serviceProvider; private readonly IOptionsFactory _optionsFactory; + private readonly IEntityDbContextFactory _dbContextFactory; public EntityFrameworkSnapshotRepositoryFactory ( IServiceProvider serviceProvider, - IOptionsFactory optionsFactory + IOptionsFactory optionsFactory, + IEntityDbContextFactory dbContextFactory ) { _serviceProvider = serviceProvider; _optionsFactory = optionsFactory; + _dbContextFactory = dbContextFactory; } public ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession) @@ -36,11 +39,11 @@ public ISnapshotRepository CreateRepository(IEntityFrameworkSession.Create(_serviceProvider, entityFrameworkSnapshotRepository); } - public async Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken) + public Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken) { - var dbContext = await TDbContext.ConstructAsync(options); + var dbContext = _dbContextFactory.Create(options); - return EntityFrameworkSession.Create(_serviceProvider, dbContext, options); + return Task.FromResult(EntityFrameworkSession.Create(_serviceProvider, dbContext, options)); } public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 60f3255a..97ac65dc 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -23,9 +23,9 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) .EnableSensitiveDataLogging(); } - public static Task> ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) + public static GenericDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) { - return Task.FromResult(new GenericDbContext(entityFrameworkSnapshotSessionOptions)); + return new GenericDbContext(entityFrameworkSnapshotSessionOptions); } protected override void OnModelCreating(ModelBuilder modelBuilder) From 6c38180544e0620d25993ab20ccf34b8145d9066 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:23:14 -0700 Subject: [PATCH 39/93] refactor: single queue for processing transactions --- .../Queries/GetTransactionQuery.cs | 35 +++++++ .../EntitySnapshotSourceProcessor.cs | 93 +++++++++++++++++++ .../Sources/Processors/ISourceProcessor.cs | 17 ++++ .../ProjectionSnapshotSourceProcessor.cs | 74 +++++++++++++++ .../Queues/BufferBlockSourceProcessorQueue.cs | 57 ++++++++++++ .../Queues/ISourceProcessorQueue.cs | 13 +++ .../Queues/ISourceProcessorQueueItem.cs | 20 ++++ .../Queues/SourceProcessorQueueItem.cs | 9 ++ .../Queues/TestModeSourceProcessorQueue.cs | 48 ++++++++++ .../BufferBlockTransactionReprocessorQueue.cs | 74 +++++++++++++++ .../ITransactionReprocessorQueue.cs | 13 +++ .../ITransactionReprocessorQueueItem.cs} | 27 +++--- .../TestModeTransactionReprocessorQueue.cs | 57 ++++++++++++ .../EntitySnapshotSourceSubscriber.cs | 29 ++++++ .../ProjectionSnapshotSourceSubscriber.cs | 28 ++++++ .../ITransactionCommandWithEntitySnapshot.cs | 8 -- .../BufferBlockTransactionProcessorQueue.cs | 54 ----------- .../ITransactionProcessorQueue.cs | 10 -- .../TestModeTransactionProcessorQueue.cs | 20 ---- ...titySnapshotTransactionCommandProcessor.cs | 42 --------- .../EntitySnapshotTransactionProcessor.cs | 61 ------------ .../ISnapshotTransactionCommandProcessor.cs | 8 -- .../Processors/ITransactionProcessor.cs | 23 ----- ...tionSnapshotTransactionCommandProcessor.cs | 54 ----------- .../ProjectionSnapshotTransactionProcessor.cs | 63 ------------- ...napshotTransactionCommandProcessorCache.cs | 18 ---- .../SnapshotTransactionProcessorBase.cs | 64 ------------- .../BufferBlockTransactionReprocessorQueue.cs | 33 ------- .../ITransactionReprocessorQueue.cs | 15 --- .../TestModeTransactionReprocessorQueue.cs | 18 ---- .../TransactionReprocessorQueueBase.cs | 74 --------------- .../TransactionProcessorSubscriber.cs | 21 ----- 32 files changed, 580 insertions(+), 600 deletions(-) create mode 100644 src/EntityDb.Common/Queries/GetTransactionQuery.cs create mode 100644 src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs create mode 100644 src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs create mode 100644 src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs create mode 100644 src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs create mode 100644 src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs create mode 100644 src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs rename src/EntityDb.Common/{Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs => Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs} (51%) create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs create mode 100644 src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs delete mode 100644 src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/ITransactionProcessorQueue.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs delete mode 100644 src/EntityDb.Common/Transactions/Subscribers/TransactionProcessorSubscriber.cs diff --git a/src/EntityDb.Common/Queries/GetTransactionQuery.cs b/src/EntityDb.Common/Queries/GetTransactionQuery.cs new file mode 100644 index 00000000..3b1c551e --- /dev/null +++ b/src/EntityDb.Common/Queries/GetTransactionQuery.cs @@ -0,0 +1,35 @@ +using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Queries.FilterBuilders; +using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Queries; + +internal sealed record GetTransactionCommandsQuery(Id TransactionId) : IAgentSignatureQuery, ICommandQuery +{ + public TFilter GetFilter(IAgentSignatureFilterBuilder builder) + { + return builder.SourceIdIn(TransactionId); + } + + public TSort? GetSort(IAgentSignatureSortBuilder builder) + { + return default; + } + + public TFilter GetFilter(ICommandFilterBuilder builder) + { + return builder.SourceIdIn(TransactionId); + } + + public TSort? GetSort(ICommandSortBuilder builder) + { + return default; + } + + public int? Skip => default; + + public int? Take => default; + + public object? Options => default; +} diff --git a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs new file mode 100644 index 00000000..bd3d1f59 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs @@ -0,0 +1,93 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Entities; +using EntityDb.Common.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.Processors; + +internal class EntitySnapshotSourceProcessor : ISourceProcessor + where TEntity : IEntity +{ + private readonly ILogger> _logger; + private readonly IEntityRepositoryFactory _entityRepositoryFactory; + private readonly string _snapshotSessionOptionsName; + private readonly string _transactionSessionOptionsName; + + public EntitySnapshotSourceProcessor + ( + ILogger> logger, + IEntityRepositoryFactory entityRepositoryFactory, + string transactionSessionOptionsName, + string snapshotSessionOptionsName + ) + { + _logger = logger; + _entityRepositoryFactory = entityRepositoryFactory; + _transactionSessionOptionsName = transactionSessionOptionsName; + _snapshotSessionOptionsName = snapshotSessionOptionsName; + } + + public async Task Process(ISource source, CancellationToken cancellationToken) + { + if (source is not ITransaction transaction) + { + throw new NotSupportedException(); + } + + await using var entityRepository = await _entityRepositoryFactory + .CreateRepository(_transactionSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); + + if (entityRepository.SnapshotRepository is null) + { + _logger.LogWarning("Snapshots not enabled, no point in processing source."); + + return; + } + + var latestEntities = new Dictionary(); + var saveEntities = new Dictionary(); + + foreach (var command in transaction.Commands) + { + var entityId = command.EntityId; + + if (!latestEntities.Remove(entityId, out var previousEntity)) + { + previousEntity = await entityRepository.SnapshotRepository.GetSnapshotOrDefault(entityId, cancellationToken); + } + + var nextEntity = (previousEntity ?? TEntity.Construct(entityId)).Reduce(command.Data); + var nextEntityPointer = nextEntity.GetPointer(); + + if (nextEntity.ShouldRecordAsLatest(previousEntity)) + { + saveEntities[entityId] = nextEntity; + } + + if (nextEntity.ShouldRecord()) + { + saveEntities[nextEntityPointer] = nextEntity; + } + + latestEntities[entityId] = nextEntity; + + cancellationToken.ThrowIfCancellationRequested(); + } + + foreach (var (entityPointer, entity) in saveEntities) + { + await entityRepository.SnapshotRepository.PutSnapshot(entityPointer, entity, cancellationToken); + } + } + + public static EntitySnapshotSourceProcessor Create(IServiceProvider serviceProvider, + string transactionSessionOptionsName, string snapshotSessionOptionsName) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, + transactionSessionOptionsName, snapshotSessionOptionsName); + } +} diff --git a/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs new file mode 100644 index 00000000..52109f42 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.Sources; + +namespace EntityDb.Common.Sources.Processors; + +/// +/// Represents a type that processes sources emitted to a . +/// +public interface ISourceProcessor +{ + /// + /// Defines the procedure for processing a given source. + /// + /// The source that has been received. + /// A cancellation token. + /// A task + Task Process(ISource source, CancellationToken cancellationToken); +} diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs new file mode 100644 index 00000000..8374cc55 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs @@ -0,0 +1,74 @@ +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Extensions; +using EntityDb.Common.Projections; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.Processors; + +internal sealed class ProjectionSnapshotSourceProcessor : ISourceProcessor + where TProjection : IProjection +{ + private readonly ILogger> _logger; + private readonly IProjectionRepositoryFactory _projectionRepositoryFactory; + private readonly string _snapshotSessionOptionsName; + private readonly string _transactionSessionOptionsName; + + public ProjectionSnapshotSourceProcessor + ( + ILogger> logger, + IProjectionRepositoryFactory projectionRepositoryFactory, + string transactionSessionOptionsName, + string snapshotSessionOptionsName + ) + { + _logger = logger; + _projectionRepositoryFactory = projectionRepositoryFactory; + _transactionSessionOptionsName = transactionSessionOptionsName; + _snapshotSessionOptionsName = snapshotSessionOptionsName; + } + + public async Task Process(ISource source, CancellationToken cancellationToken) + { + await using var projectionRepository = await _projectionRepositoryFactory + .CreateRepository(_transactionSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); + + if (projectionRepository.SnapshotRepository is null) + { + _logger.LogWarning("Snapshots not enabled, no point in processing source."); + + return; + } + + foreach (var projectionId in TProjection.EnumerateProjectionIds(source)) + { + var previousProjection = await projectionRepository.SnapshotRepository + .GetSnapshotOrDefault(projectionId, cancellationToken); + + var nextProjection = (previousProjection ?? TProjection.Construct(projectionId)).Reduce(source); + var nextProjectionPointer = nextProjection.GetPointer(); + + if (nextProjection.ShouldRecordAsLatest(previousProjection)) + { + await projectionRepository.SnapshotRepository.PutSnapshot(projectionId, nextProjection, cancellationToken); + } + + if (nextProjection.ShouldRecord()) + { + await projectionRepository.SnapshotRepository.PutSnapshot(nextProjectionPointer, nextProjection, cancellationToken); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + } + + public static ProjectionSnapshotSourceProcessor Create(IServiceProvider serviceProvider, + string transactionSessionOptionsName, string snapshotSessionOptionsName) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, + transactionSessionOptionsName, + snapshotSessionOptionsName); + } +} diff --git a/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs new file mode 100644 index 00000000..82530eac --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks.Dataflow; + +namespace EntityDb.Common.Sources.Processors.Queues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal class BufferBlockSourceProcessorQueue : BackgroundService, ISourceProcessorQueue +{ + private readonly BufferBlock _bufferBlock = new(); + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public BufferBlockSourceProcessorQueue(ILogger logger, IServiceScopeFactory serviceScopeFactory) + { + _logger = logger; + _serviceScopeFactory = serviceScopeFactory; + } + + public void Enqueue(ISourceProcessorQueueItem item) + { + _bufferBlock.Post(item); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (await _bufferBlock.OutputAvailableAsync(stoppingToken)) + { + var item = await _bufferBlock.ReceiveAsync(stoppingToken); + + await using var serviceScope = _serviceScopeFactory.CreateAsyncScope(); + + using var logScope = _logger.BeginScope(new KeyValuePair[] + { + new("SourceProcessorType", item.SourceProcessorType.Name), + new("SourceId", item.Source.Id.Value) + }); + + try + { + var sourceProcessor = (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(item.SourceProcessorType); + + _logger.LogDebug("Started processing source"); + + await sourceProcessor.Process(item.Source, stoppingToken); + + _logger.LogDebug("Finished processing source"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Error occurred while processing source"); + } + } + } +} diff --git a/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueue.cs new file mode 100644 index 00000000..cc8c5b6c --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueue.cs @@ -0,0 +1,13 @@ +namespace EntityDb.Common.Sources.Processors.Queues; + +/// +/// A service for queueing source processing work +/// +public interface ISourceProcessorQueue +{ + /// + /// Adds an item to the queue + /// + /// The work to be queued + void Enqueue(ISourceProcessorQueueItem item); +} diff --git a/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs b/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs new file mode 100644 index 00000000..0693dad7 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Sources; + +namespace EntityDb.Common.Sources.Processors.Queues; + +/// +/// An item of work for a +/// +public interface ISourceProcessorQueueItem +{ + /// + /// The type of the source processor, which *must* implement + /// . + /// + Type SourceProcessorType { get; } + + /// + /// The source to be processed. + /// + ISource Source { get; } +} diff --git a/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs new file mode 100644 index 00000000..828b781b --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.Sources; + +namespace EntityDb.Common.Sources.Processors.Queues; + +internal class SourceProcessorQueueItem : ISourceProcessorQueueItem +{ + public required Type SourceProcessorType { get; init; } + public required ISource Source { get; init; } +} diff --git a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs new file mode 100644 index 00000000..7b414f5d --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs @@ -0,0 +1,48 @@ +using EntityDb.Common.Sources.Processors; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.Processors.Queues; + +internal class TestModeSourceProcessorQueue : ISourceProcessorQueue +{ + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public TestModeSourceProcessorQueue(ILogger logger, IServiceScopeFactory serviceScopeFactory) + { + _logger = logger; + _serviceScopeFactory = serviceScopeFactory; + } + + private async Task Process(ISourceProcessorQueueItem item, CancellationToken cancellationToken) + { + await using var serviceScope = _serviceScopeFactory.CreateAsyncScope(); + + using var logScope = _logger.BeginScope(new KeyValuePair[] + { + new("SourceProcessorType", item.SourceProcessorType.Name), + new("SourceId", item.Source.Id.Value), + }); + + try + { + var sourceProcessor = (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(item.SourceProcessorType); + + _logger.LogDebug("Started processing source"); + + await sourceProcessor.Process(item.Source, cancellationToken); + + _logger.LogDebug("Finished processing source"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Error occurred while processing source"); + } + } + + public void Enqueue(ISourceProcessorQueueItem item) + { + Task.Run(() => Process(item, default)).Wait(); + } +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs new file mode 100644 index 00000000..84e0ca7a --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs @@ -0,0 +1,74 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks.Dataflow; + +namespace EntityDb.Common.Sources.ReprocessorQueues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal class BufferBlockTransactionReprocessorQueue : BackgroundService, ITransactionReprocessorQueue +{ + private readonly BufferBlock _bufferBlock = new(); + private readonly ILogger _logger; + private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly ISourceProcessorQueue _transactionProcessorQueue; + + public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue transactionProcessorQueue) + { + _logger = logger; + _transactionRepositoryFactory = transactionRepositoryFactory; + _transactionProcessorQueue = transactionProcessorQueue; + } + + public void Enqueue(ITransactionReprocessorQueueItem reprocessTransactionsRequest) + { + _bufferBlock.Post(reprocessTransactionsRequest); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (await _bufferBlock.OutputAvailableAsync(stoppingToken)) + { + var reprocessTransactionsRequest = await _bufferBlock.ReceiveAsync(stoppingToken); + + await Process(reprocessTransactionsRequest, stoppingToken); + } + } + + protected async Task Process(ITransactionReprocessorQueueItem item, CancellationToken cancellationToken) + { + try + { + _logger.LogDebug("Started reprocessing transactions"); + + await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(item.TransactionSessionOptionsName, cancellationToken); + + var transactionIds = await transactionRepository + .EnumerateTransactionIds(item.Query, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var transactionId in transactionIds) + { + await Task.Delay(item.EnqueueDelay, cancellationToken); + + var transaction = await transactionRepository + .GetTransaction(transactionId, cancellationToken); + + _transactionProcessorQueue.Enqueue(new SourceProcessorQueueItem + { + SourceProcessorType = item.TransactionProcessorType, + Source = transaction, + }); + } + + _logger.LogDebug("Finished reprocessing transactions"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess transactions"); + } + } +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs new file mode 100644 index 00000000..198719db --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs @@ -0,0 +1,13 @@ +namespace EntityDb.Common.Sources.ReprocessorQueues; + +/// +/// A queue that reprocesses transactions +/// +public interface ITransactionReprocessorQueue +{ + /// + /// Enqueues the request to reprocess transactions + /// + /// + void Enqueue(ITransactionReprocessorQueueItem item); +} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs similarity index 51% rename from src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs rename to src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs index 13ee77a2..5af3780b 100644 --- a/src/EntityDb.Common/Transactions/Subscribers/Reprocessors/IReprocessTransactionRequest.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs @@ -1,14 +1,13 @@ using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Processors; -using Microsoft.Extensions.Logging; +using EntityDb.Common.Sources.Processors; -namespace EntityDb.Common.Transactions.Subscribers.Reprocessors; +namespace EntityDb.Common.Sources.ReprocessorQueues; /// -/// Represents a request for a to reprocess transactions. +/// Represents a request for a to reprocess transactions. /// -public interface IReprocessTransactionsRequest +public interface ITransactionReprocessorQueueItem { /// /// The name of the transaction session options passed to @@ -16,24 +15,24 @@ public interface IReprocessTransactionsRequest string TransactionSessionOptionsName { get; } /// - /// Determines which transaction processor needs to reprocess the transactions, based on the value of . + /// The type of the transaction process, which *must* + /// implement . /// - string TransactionProcessorIdentifier { get; } + Type TransactionProcessorType { get; } /// /// Determines which transactions need to be reprocessed. /// - ICommandQuery CommandQuery { get; } + IQuery Query { get; } /// - /// If true, then stop executing this request when the transaction processor throws an exception. - /// Otherwise, execute this request for all matching transactions. + /// Determines how long to wait between each call to enqueue. /// - bool BreakOnThrow { get; } + TimeSpan EnqueueDelay { get; } /// - /// Converts this request into a log-friendly format. + /// If true, then stop executing this request when the transaction processor throws an exception. + /// Otherwise, execute this request for all matching transactions. /// - /// The object that will be passed to - object ToLogScopeState(); + bool BreakOnThrow { get; } } diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs new file mode 100644 index 00000000..baebc6b7 --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs @@ -0,0 +1,57 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.ReprocessorQueues; + +internal class TestModeTransactionReprocessorQueue : ITransactionReprocessorQueue +{ + private readonly ILogger _logger; + private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly ISourceProcessorQueue _transactionProcessorQueue; + + public TestModeTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue transactionProcessorQueue) + { + _logger = logger; + _transactionRepositoryFactory = transactionRepositoryFactory; + _transactionProcessorQueue = transactionProcessorQueue; + } + + public void Enqueue(ITransactionReprocessorQueueItem reprocessTransactionsRequest) + { + Task.Run(() => Process(reprocessTransactionsRequest, default)); + } + + protected async Task Process(ITransactionReprocessorQueueItem item, CancellationToken cancellationToken) + { + try + { + _logger.LogDebug("Started reprocessing transactions"); + + await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(item.TransactionSessionOptionsName, cancellationToken); + + var transactionIds = await transactionRepository + .EnumerateTransactionIds(item.Query, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var transactionId in transactionIds) + { + var transaction = await transactionRepository + .GetTransaction(transactionId, cancellationToken); + + _transactionProcessorQueue.Enqueue(new SourceProcessorQueueItem + { + SourceProcessorType = item.TransactionProcessorType, + Source = transaction, + }); + } + + _logger.LogDebug("Finished reprocessing transactions"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess transactions"); + } + } +} diff --git a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs new file mode 100644 index 00000000..95f49466 --- /dev/null +++ b/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs @@ -0,0 +1,29 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Transactions; +using EntityDb.Common.Entities; +using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.Sources.Processors.Queues; + +namespace EntityDb.Common.Sources.Subscribers; + +internal class EntitySnapshotSourceSubscriber : ISourceSubscriber + where TEntity : IEntity +{ + private readonly ISourceProcessorQueue _transactionProcessorQueue; + + public EntitySnapshotSourceSubscriber(ISourceProcessorQueue transactionProcessorQueue) + { + _transactionProcessorQueue = transactionProcessorQueue; + } + + public void Notify(ISource source) + { + if (source is not ITransaction transaction || !transaction.Commands.Any(command => TEntity.CanReduce(command.Data))) + { + return; + } + + _transactionProcessorQueue.Enqueue>(source); + } +} diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs new file mode 100644 index 00000000..0c751675 --- /dev/null +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs @@ -0,0 +1,28 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Extensions; +using EntityDb.Common.Projections; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.Sources.Processors.Queues; + +namespace EntityDb.Common.Sources.Subscribers; + +internal class ProjectionSnapshotSourceSubscriber : ISourceSubscriber + where TProjection : IProjection +{ + private readonly ISourceProcessorQueue _transactionProcessorQueue; + + public ProjectionSnapshotSourceSubscriber(ISourceProcessorQueue transactionProcessorQueue) + { + _transactionProcessorQueue = transactionProcessorQueue; + } + + public void Notify(ISource source) + { + if (!TProjection.EnumerateProjectionIds(source).Any()) + { + return; + } + + _transactionProcessorQueue.Enqueue>(source); + } +} diff --git a/src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs b/src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs deleted file mode 100644 index 4131816a..00000000 --- a/src/EntityDb.Common/Transactions/ITransactionCommandWithEntitySnapshot.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EntityDb.Abstractions.Transactions; - -namespace EntityDb.Common.Transactions; - -internal interface ITransactionCommandWithSnapshot : ITransactionCommand -{ - object Snapshot { get; } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs deleted file mode 100644 index 79ebabb9..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionProcessorQueue.cs +++ /dev/null @@ -1,54 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Processors; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks.Dataflow; - -namespace EntityDb.Common.Transactions.Subscribers.ProcessorQueues; - -[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] -internal class BufferBlockTransactionProcessorQueue : BackgroundService, ITransactionProcessorQueue - where TTransactionProcessor : ITransactionProcessor -{ - private readonly BufferBlock _transactionQueue = new(); - private readonly ILogger> _logger; - private readonly TTransactionProcessor _transactionProcessor; - - public BufferBlockTransactionProcessorQueue(ILogger> logger, TTransactionProcessor transactionProcessor) - { - _logger = logger; - _transactionProcessor = transactionProcessor; - } - - public void Enqueue(ITransaction transaction) - { - _transactionQueue.Post(transaction); - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (await _transactionQueue.OutputAvailableAsync(stoppingToken)) - { - var transaction = await _transactionQueue.ReceiveAsync(stoppingToken); - - using var _ = _logger.BeginScope(new KeyValuePair[] - { - new("TransactionId", transaction.Id.Value) - }); - - try - { - _logger.LogDebug("Started processing transaction"); - - await _transactionProcessor.ProcessTransaction(transaction, stoppingToken); - - _logger.LogDebug("Finished processing transaction"); - } - catch (Exception exception) - { - _logger.LogError(exception, "Error occurred while processing transaction"); - } - } - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/ITransactionProcessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/ITransactionProcessorQueue.cs deleted file mode 100644 index 52b0ec44..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/ITransactionProcessorQueue.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Processors; - -namespace EntityDb.Common.Transactions.Subscribers.ProcessorQueues; - -internal interface ITransactionProcessorQueue - where TTransactionProcessor : ITransactionProcessor -{ - void Enqueue(ITransaction transaction); -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs deleted file mode 100644 index 02b8e8fd..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionProcessorQueue.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Processors; - -namespace EntityDb.Common.Transactions.Subscribers.ProcessorQueues; - -internal class TestModeTransactionProcessorQueue : ITransactionProcessorQueue - where TTransactionProcessor : ITransactionProcessor -{ - private readonly TTransactionProcessor _transactionProcessor; - - public TestModeTransactionProcessorQueue(TTransactionProcessor transactionProcessor) - { - _transactionProcessor = transactionProcessor; - } - - public void Enqueue(ITransaction transaction) - { - Task.Run(() => _transactionProcessor.ProcessTransaction(transaction, default)).Wait(); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs deleted file mode 100644 index 20f58129..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionCommandProcessor.cs +++ /dev/null @@ -1,42 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal class EntitySnapshotTransactionCommandProcessor : ISnapshotTransactionCommandProcessor -{ - private readonly IEntityRepository _entityRepository; - private readonly SnapshotTransactionCommandProcessorCache _snapshotTransactionCommandProcessorCache; - - public EntitySnapshotTransactionCommandProcessor - ( - IEntityRepository entityRepository, - SnapshotTransactionCommandProcessorCache snapshotTransactionCommandProcessorCache - ) - { - _entityRepository = entityRepository; - _snapshotTransactionCommandProcessorCache = snapshotTransactionCommandProcessorCache; - } - - public async Task<(TEntity?, TEntity)?> GetSnapshots(ITransaction transaction, ITransactionCommand transactionCommand, CancellationToken cancellationToken) - { - if (transactionCommand is not ITransactionCommandWithSnapshot transactioncommandWithSnapshot || transactioncommandWithSnapshot.Snapshot is not TEntity nextSnapshot) - { - return null; - } - - var previousLatestPointer = transactionCommand.EntityId + - transactionCommand.EntityVersionNumber.Previous(); - - TEntity? previousLatestSnapshot = default; - - if (previousLatestPointer.VersionNumber != VersionNumber.MinValue) - { - previousLatestSnapshot = _snapshotTransactionCommandProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? - await _entityRepository.GetSnapshot(previousLatestPointer, cancellationToken); - } - - return (previousLatestSnapshot, nextSnapshot); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs deleted file mode 100644 index 861ef094..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs +++ /dev/null @@ -1,61 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Entities; -using Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal class EntitySnapshotTransactionProcessor : SnapshotTransactionProcessorBase - where TEntity : IEntity -{ - private readonly IEntityRepositoryFactory _entityRepositoryFactory; - private readonly string _snapshotSessionOptionsName; - private readonly string _transactionSessionOptionsName; - - public EntitySnapshotTransactionProcessor - ( - IEntityRepositoryFactory entityRepositoryFactory, - string transactionSessionOptionsName, - string snapshotSessionOptionsName - ) : base($"EntitySnapshot<{typeof(TEntity).Name}>") - { - _entityRepositoryFactory = entityRepositoryFactory; - _transactionSessionOptionsName = transactionSessionOptionsName; - _snapshotSessionOptionsName = snapshotSessionOptionsName; - } - - public override async Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken) - { - await using var entityRepository = await _entityRepositoryFactory.CreateRepository( - _transactionSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); - - if (entityRepository.SnapshotRepository is null) - { - return; - } - - var snapshotTransactionCommandProcessorCache = new SnapshotTransactionCommandProcessorCache(); - - var snapshotCommandProcessor = new EntitySnapshotTransactionCommandProcessor - ( - entityRepository, - snapshotTransactionCommandProcessorCache - ); - - await ProcessTransactionCommands - ( - entityRepository.SnapshotRepository, - snapshotTransactionCommandProcessorCache, - transaction, - snapshotCommandProcessor, - cancellationToken - ); - } - - public static EntitySnapshotTransactionProcessor Create(IServiceProvider serviceProvider, - string transactionSessionOptionsName, string snapshotSessionOptionsName) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionSessionOptionsName, snapshotSessionOptionsName); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs deleted file mode 100644 index c2e477ee..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ISnapshotTransactionCommandProcessor.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EntityDb.Abstractions.Transactions; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal interface ISnapshotTransactionCommandProcessor -{ - Task<(TSnapshot?, TSnapshot)?> GetSnapshots(ITransaction transaction, ITransactionCommand transactionCommand, CancellationToken cancellationToken); -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs deleted file mode 100644 index d983df98..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Reprocessors; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -/// -/// Represents a type that processes transactions emitted to a . -/// -public interface ITransactionProcessor -{ - /// - /// Used for targeted reprocessing with . - /// - string Identifier { get; } - - /// - /// Defines the procedure for processing a given transaction. - /// - /// The transaction that has been received. - /// A cancellation token. - /// A task - Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken); -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs deleted file mode 100644 index 69edf1da..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionCommandProcessor.cs +++ /dev/null @@ -1,54 +0,0 @@ -using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Projections; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal sealed class ProjectionSnapshotTransactionCommandProcessor : ISnapshotTransactionCommandProcessor - where TProjection : IProjection -{ - private readonly IProjectionRepository _projectionRepository; - private readonly SnapshotTransactionCommandProcessorCache _snapshotTransactionCommandProcessorCache; - - public ProjectionSnapshotTransactionCommandProcessor - ( - IProjectionRepository projectionRepository, - SnapshotTransactionCommandProcessorCache snapshotTransactionCommandProcessorCache - ) - { - _projectionRepository = projectionRepository; - _snapshotTransactionCommandProcessorCache = snapshotTransactionCommandProcessorCache; - } - - public async Task<(TProjection?, TProjection)?> GetSnapshots - ( - ITransaction transaction, - ITransactionCommand transactionCommand, - CancellationToken cancellationToken - ) - { - var projectionId = _projectionRepository.GetProjectionIdOrDefault(transaction, transactionCommand); - - if (projectionId is null) - { - return null; - } - - var previousLatestPointer = projectionId.Value + transactionCommand.EntityVersionNumber.Previous(); - - TProjection? previousLatestSnapshot = default; - - if (previousLatestPointer.VersionNumber != VersionNumber.MinValue) - { - previousLatestSnapshot = _snapshotTransactionCommandProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? - await _projectionRepository.GetSnapshot(previousLatestPointer, - cancellationToken); - } - - var nextSnapshot = - (previousLatestSnapshot ?? TProjection.Construct(projectionId.Value)).Reduce(transaction, transactionCommand); - - return (previousLatestSnapshot, nextSnapshot); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs deleted file mode 100644 index fb72952e..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs +++ /dev/null @@ -1,63 +0,0 @@ -using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Projections; -using Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal sealed class - ProjectionSnapshotTransactionProcessor : SnapshotTransactionProcessorBase - where TProjection : IProjection -{ - private readonly IProjectionRepositoryFactory _projectionRepositoryFactory; - private readonly string _snapshotSessionOptionsName; - private readonly string _transactionSessionOptionsName; - - public ProjectionSnapshotTransactionProcessor - ( - IProjectionRepositoryFactory projectionRepositoryFactory, - string transactionSessionOptionsName, - string snapshotSessionOptionsName - ) : base($"ProjectionSnapshot<{typeof(TProjection).Name}>") - { - _projectionRepositoryFactory = projectionRepositoryFactory; - _transactionSessionOptionsName = transactionSessionOptionsName; - _snapshotSessionOptionsName = snapshotSessionOptionsName; - } - - public override async Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken) - { - await using var projectionRepository = await _projectionRepositoryFactory.CreateRepository( - _transactionSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); - - if (projectionRepository.SnapshotRepository is null) - { - return; - } - - var snapshotTransactionCommandProcessorCache = new SnapshotTransactionCommandProcessorCache(); - - var projectionSnapshotTransactionCommandProcessor = new ProjectionSnapshotTransactionCommandProcessor - ( - projectionRepository, - snapshotTransactionCommandProcessorCache - ); - - await ProcessTransactionCommands - ( - projectionRepository.SnapshotRepository, - snapshotTransactionCommandProcessorCache, - transaction, - projectionSnapshotTransactionCommandProcessor, - cancellationToken - ); - } - - public static ProjectionSnapshotTransactionProcessor Create(IServiceProvider serviceProvider, - string transactionSessionOptionsName, string snapshotSessionOptionsName) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionSessionOptionsName, - snapshotSessionOptionsName); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs deleted file mode 100644 index a4b2d1f1..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionCommandProcessorCache.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal class SnapshotTransactionCommandProcessorCache -{ - private readonly Dictionary _cache = new(); - - public void PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot) - { - _cache[snapshotPointer] = snapshot; - } - - public TSnapshot? GetSnapshotOrDefault(Pointer snapshotPointer) - { - return _cache.GetValueOrDefault(snapshotPointer); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs deleted file mode 100644 index ee010a4f..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs +++ /dev/null @@ -1,64 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Snapshots; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal abstract class SnapshotTransactionProcessorBase : ITransactionProcessor - where TSnapshot : ISnapshot -{ - public string Identifier { get; } - - protected SnapshotTransactionProcessorBase(string identifier) - { - Identifier = identifier; - } - - public abstract Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken); - - protected static async Task ProcessTransactionCommands - ( - ISnapshotRepository snapshotRepository, - SnapshotTransactionCommandProcessorCache snapshotTransactionCommandProcessorCache, - ITransaction transaction, - ISnapshotTransactionCommandProcessor snapshotTransactionCommandProcessor, - CancellationToken cancellationToken - ) - { - var putQueue = new Dictionary(); - - foreach (var transactionCommand in transaction.Commands) - { - var snapshots = await snapshotTransactionCommandProcessor.GetSnapshots(transaction, transactionCommand, cancellationToken); - - if (snapshots is not var (previousLatestSnapshot, nextSnapshot)) - { - continue; - } - - var snapshotId = nextSnapshot.GetId(); - - if (nextSnapshot.ShouldRecordAsLatest(previousLatestSnapshot)) - { - putQueue[snapshotId] = nextSnapshot; - } - else - { - snapshotTransactionCommandProcessorCache.PutSnapshot(snapshotId, nextSnapshot); - } - - if (nextSnapshot.ShouldRecord()) - { - putQueue[snapshotId + nextSnapshot.GetVersionNumber()] = nextSnapshot; - } - - cancellationToken.ThrowIfCancellationRequested(); - } - - foreach (var (snapshotPointer, snapshot) in putQueue) - { - await snapshotRepository.PutSnapshot(snapshotPointer, snapshot, cancellationToken); - } - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs deleted file mode 100644 index 98e1221c..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Processors; -using EntityDb.Common.Transactions.Subscribers.Reprocessors; -using Microsoft.Extensions.Logging; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks.Dataflow; - -namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; - -[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] -internal class BufferBlockTransactionReprocessorQueue : TransactionReprocessorQueueBase, ITransactionReprocessorQueue -{ - private readonly BufferBlock _bufferBlock = new(); - - public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) : base(logger, transactionRepositoryFactory, transactionProcessors) - { - } - - public void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest) - { - _bufferBlock.Post(reprocessTransactionsRequest); - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (await _bufferBlock.OutputAvailableAsync(stoppingToken)) - { - var reprocessTransactionsRequest = await _bufferBlock.ReceiveAsync(stoppingToken); - - await Process(reprocessTransactionsRequest, stoppingToken); - } - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs deleted file mode 100644 index d41079bc..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/ITransactionReprocessorQueue.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Common.Transactions.Subscribers.Reprocessors; - -namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; - -/// -/// A queue that reprocesses transactions -/// -public interface ITransactionReprocessorQueue -{ - /// - /// Enqueues the request to reprocess transactions - /// - /// - void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest); -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs deleted file mode 100644 index c37587c0..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.Processors; -using EntityDb.Common.Transactions.Subscribers.Reprocessors; -using Microsoft.Extensions.Logging; - -namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; - -internal class TestModeTransactionReprocessorQueue : TransactionReprocessorQueueBase, ITransactionReprocessorQueue -{ - public TestModeTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) : base(logger, transactionRepositoryFactory, transactionProcessors) - { - } - - public void Enqueue(IReprocessTransactionsRequest reprocessTransactionsRequest) - { - Task.Run(() => Process(reprocessTransactionsRequest, default)); - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs b/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs deleted file mode 100644 index 84a87489..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ReprocessorQueues/TransactionReprocessorQueueBase.cs +++ /dev/null @@ -1,74 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Extensions; -using EntityDb.Common.Transactions.Subscribers.Processors; -using EntityDb.Common.Transactions.Subscribers.Reprocessors; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; - -internal abstract class TransactionReprocessorQueueBase : BackgroundService -{ - private readonly ILogger _logger; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; - private readonly IEnumerable _transactionProcessors; - - protected TransactionReprocessorQueueBase(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, IEnumerable transactionProcessors) - { - _logger = logger; - _transactionRepositoryFactory = transactionRepositoryFactory; - _transactionProcessors = transactionProcessors; - } - - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - // I don't like that I have to do this, but it's the only decent way - // to share the Process method between BufferBlock and TestMode implementations - throw new NotSupportedException(); - } - - protected async Task Process(IReprocessTransactionsRequest reprocessTransactionsRequest, CancellationToken cancellationToken) - { - using var outerScope = _logger.BeginScope(reprocessTransactionsRequest.ToLogScopeState()); - - try - { - _logger.LogDebug("Started reprocessing transactions"); - - var transactionProcessor = _transactionProcessors - .Single(transactionProcessor => transactionProcessor.Identifier == reprocessTransactionsRequest.TransactionProcessorIdentifier); - - await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(reprocessTransactionsRequest.TransactionSessionOptionsName, cancellationToken); - - var transactions = transactionRepository.EnumerateTransactions(reprocessTransactionsRequest.CommandQuery, cancellationToken); - - await foreach (var transaction in transactions) - { - using var innerScope = _logger.BeginScope(new KeyValuePair[] - { - new("TransactionId", transaction.Id.Value), - }); - - try - { - await transactionProcessor.ProcessTransaction(transaction, cancellationToken); - } - catch (Exception exception) - { - _logger.LogError(exception, "Failed to reprocess transaction"); - - if (reprocessTransactionsRequest.BreakOnThrow) - { - break; - } - } - } - - _logger.LogDebug("Finished reprocessing transactions"); - } - catch (Exception exception) - { - _logger.LogError(exception, "Failed to reprocess transactions"); - } - } -} diff --git a/src/EntityDb.Common/Transactions/Subscribers/TransactionProcessorSubscriber.cs b/src/EntityDb.Common/Transactions/Subscribers/TransactionProcessorSubscriber.cs deleted file mode 100644 index d2da2ac7..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/TransactionProcessorSubscriber.cs +++ /dev/null @@ -1,21 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions.Subscribers.ProcessorQueues; -using EntityDb.Common.Transactions.Subscribers.Processors; - -namespace EntityDb.Common.Transactions.Subscribers; - -internal sealed class TransactionProcessorSubscriber : ITransactionSubscriber - where TTransactionProcessor : ITransactionProcessor -{ - private readonly ITransactionProcessorQueue _transactionProcessorQueue; - - public TransactionProcessorSubscriber(ITransactionProcessorQueue transactionProcessorQueue) - { - _transactionProcessorQueue = transactionProcessorQueue; - } - - public void Notify(ITransaction transaction) - { - _transactionProcessorQueue.Enqueue(transaction); - } -} From 060b9751723d4174950a2a2c729fc40257d1749a Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:25:49 -0700 Subject: [PATCH 40/93] chore: upgrade packages, images --- .../EntityDb.EntityFramework.csproj | 2 +- .../packages.lock.json | 24 +- src/EntityDb.MongoDb/EntityDb.MongoDb.csproj | 2 +- .../Envelopes/BsonDocumentEnvelopeService.cs | 5 + src/EntityDb.MongoDb/packages.lock.json | 48 ++- src/EntityDb.Npgsql/EntityDb.Npgsql.csproj | 2 +- src/EntityDb.Npgsql/packages.lock.json | 14 +- src/EntityDb.Provisioner/packages.lock.json | 61 ++- src/EntityDb.Redis/EntityDb.Redis.csproj | 2 +- src/EntityDb.Redis/packages.lock.json | 99 +---- test/Directory.Build.props | 20 +- .../DatabaseContainerFixture.cs | 56 +-- .../EntityDb.Common.Tests.csproj | 16 +- test/EntityDb.Common.Tests/TestsBase.cs | 25 +- test/EntityDb.Common.Tests/packages.lock.json | 398 ++++++++--------- .../EntityDb.MongoDb.Tests.csproj | 10 - .../EntityDb.MongoDb.Tests/packages.lock.json | 402 +++++++++--------- .../EntityDb.Mvc.Tests.csproj | 10 - test/EntityDb.Mvc.Tests/packages.lock.json | 402 +++++++++--------- .../EntityDb.Redis.Tests.csproj | 10 - test/EntityDb.Redis.Tests/packages.lock.json | 402 +++++++++--------- 21 files changed, 965 insertions(+), 1045 deletions(-) diff --git a/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj b/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj index 2ec2f457..cd4bfb7d 100644 --- a/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj +++ b/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/EntityDb.EntityFramework/packages.lock.json b/src/EntityDb.EntityFramework/packages.lock.json index 74b6287f..63d19196 100644 --- a/src/EntityDb.EntityFramework/packages.lock.json +++ b/src/EntityDb.EntityFramework/packages.lock.json @@ -4,11 +4,11 @@ "net7.0": { "Microsoft.EntityFrameworkCore.Relational": { "type": "Direct", - "requested": "[7.0.0, )", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "requested": "[7.0.10, )", + "resolved": "7.0.10", + "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.EntityFrameworkCore": "7.0.10", "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" } }, @@ -28,11 +28,11 @@ }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "resolved": "7.0.10", + "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", "Microsoft.Extensions.Caching.Memory": "7.0.0", "Microsoft.Extensions.DependencyInjection": "7.0.0", "Microsoft.Extensions.Logging": "7.0.0" @@ -40,13 +40,13 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + "resolved": "7.0.10", + "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + "resolved": "7.0.10", + "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", diff --git a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj index fdde2e40..f979ad1d 100644 --- a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj +++ b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs b/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs index 198f3a66..ebab2978 100644 --- a/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs +++ b/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; namespace EntityDb.MongoDb.Envelopes; @@ -19,7 +20,11 @@ internal class MongoDbEnvelopeService : IEnvelopeService static MongoDbEnvelopeService() { + BsonSerializer.RegisterSerializer(new ObjectSerializer(ObjectSerializer.AllAllowedTypes)); BsonSerializer.RegisterSerializer(new EnvelopeSerializer()); + BsonSerializer.RegisterSerializer(new IdSerializer()); + BsonSerializer.RegisterSerializer(new TimeStampSerializer()); + BsonSerializer.RegisterSerializer(new VersionNumberSerializer()); } public MongoDbEnvelopeService diff --git a/src/EntityDb.MongoDb/packages.lock.json b/src/EntityDb.MongoDb/packages.lock.json index 8e4e3579..9076ded1 100644 --- a/src/EntityDb.MongoDb/packages.lock.json +++ b/src/EntityDb.MongoDb/packages.lock.json @@ -4,14 +4,14 @@ "net7.0": { "MongoDB.Driver": { "type": "Direct", - "requested": "[2.18.0, )", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "requested": "[2.21.0, )", + "resolved": "2.21.0", + "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.21.0", + "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0" } }, "System.Linq.Async": { @@ -23,6 +23,19 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -57,21 +70,23 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.21.0", + "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.21.0", + "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -80,8 +95,8 @@ }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "SharpCompress": { "type": "Transitive", @@ -98,6 +113,11 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "5.0.0", diff --git a/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj b/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj index 26572694..81e843d9 100644 --- a/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj +++ b/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/EntityDb.Npgsql/packages.lock.json b/src/EntityDb.Npgsql/packages.lock.json index 1116abe9..9e5972d9 100644 --- a/src/EntityDb.Npgsql/packages.lock.json +++ b/src/EntityDb.Npgsql/packages.lock.json @@ -4,12 +4,11 @@ "net7.0": { "Npgsql": { "type": "Direct", - "requested": "[7.0.0, )", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", + "requested": "[7.0.4, )", + "resolved": "7.0.4", + "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "System.Linq.Async": { @@ -31,11 +30,6 @@ "resolved": "6.0.0", "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, "entitydb.abstractions": { "type": "Project", "dependencies": { diff --git a/src/EntityDb.Provisioner/packages.lock.json b/src/EntityDb.Provisioner/packages.lock.json index be1e68a4..506a542b 100644 --- a/src/EntityDb.Provisioner/packages.lock.json +++ b/src/EntityDb.Provisioner/packages.lock.json @@ -26,6 +26,19 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -60,32 +73,34 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.21.0", + "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.21.0", + "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.21.0", + "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.21.0", + "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -94,16 +109,15 @@ }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "Npgsql": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", + "resolved": "7.0.4", + "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "SharpCompress": { @@ -121,10 +135,15 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -169,7 +188,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.0, )", + "MongoDB.Driver": "[2.21.0, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -177,7 +196,7 @@ "type": "Project", "dependencies": { "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", + "Npgsql": "[7.0.4, )", "System.Linq.Async": "[6.0.1, )" } }, diff --git a/src/EntityDb.Redis/EntityDb.Redis.csproj b/src/EntityDb.Redis/EntityDb.Redis.csproj index 83f4a193..01d9f39e 100644 --- a/src/EntityDb.Redis/EntityDb.Redis.csproj +++ b/src/EntityDb.Redis/EntityDb.Redis.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/EntityDb.Redis/packages.lock.json b/src/EntityDb.Redis/packages.lock.json index 7b3af677..5ca05b22 100644 --- a/src/EntityDb.Redis/packages.lock.json +++ b/src/EntityDb.Redis/packages.lock.json @@ -4,12 +4,11 @@ "net7.0": { "StackExchange.Redis": { "type": "Direct", - "requested": "[2.6.70, )", - "resolved": "2.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "requested": "[2.6.122, )", + "resolved": "2.6.122", + "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.Linq.Async": { @@ -26,105 +25,19 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" - }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" - } - }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "2.2.2", - "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { "System.IO.Pipelines": "5.0.1" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "5.0.0", - "System.Security.Permissions": "5.0.0" - } - }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Configuration.ConfigurationManager": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" - } - }, "System.IO.Pipelines": { "type": "Transitive", "resolved": "5.0.1", "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==" - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } - }, "entitydb.abstractions": { "type": "Project", "dependencies": { diff --git a/test/Directory.Build.props b/test/Directory.Build.props index dc86de2d..ab7359f9 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -14,17 +14,17 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs index f7298901..f0cefb39 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs @@ -1,20 +1,15 @@ using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; +using Testcontainers.PostgreSql; +using Testcontainers.Redis; using Xunit; namespace EntityDb.Common.Tests; public class DatabaseContainerFixture : IAsyncLifetime { - private static readonly RedisTestcontainerConfiguration _redisConfiguration = new("redis:7.0.2"); - - private static readonly MongoDbTestcontainerConfiguration _mongoDbConfiguration = new("mongo:5.0.9") - { - Database = "entitydb", - Username = null, - Password = null - }; + public const string OmniParameter = "entitydb"; private static readonly string DockerVolumeMongoDbInit = Path.Combine ( @@ -24,32 +19,39 @@ public class DatabaseContainerFixture : IAsyncLifetime "Init" ); - private static readonly PostgreSqlTestcontainerConfiguration _postgreSqlConfiguration = new("postgres:12.6") - { - Database = "entitydb", - Username = "entitydb", - Password = "entitydb", - }; - - public RedisTestcontainerConfiguration RedisConfiguration => _redisConfiguration; + public RedisContainer RedisContainer { get; } = new RedisBuilder() + .WithImage("redis:7.2.0") + .Build(); - public MongoDbTestcontainerConfiguration MongoDbConfiguration => _mongoDbConfiguration; - public PostgreSqlTestcontainerConfiguration PostgreSqlConfiguration => _postgreSqlConfiguration; + private sealed class WaitUntil : IWaitUntil + { + private static readonly string[] LineEndings = new string[2] { "\r\n", "\n" }; - public RedisTestcontainer RedisContainer { get; } = new TestcontainersBuilder() - .WithDatabase(_redisConfiguration) - .Build(); + public async Task UntilAsync(IContainer container) + { + var (text, text2) = await container.GetLogs(default, default, timestampsEnabled: false).ConfigureAwait(continueOnCapturedContext: false); + return 2.Equals(Array.Empty().Concat(text.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)).Concat(text2.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) + .Count((string line) => line.Contains("Waiting for connections"))); + } + } - public MongoDbTestcontainer MongoDbContainer { get; } = new TestcontainersBuilder() - .WithDatabase(_mongoDbConfiguration) + public IContainer MongoDbContainer { get; } = new ContainerBuilder() + .WithImage("mongo:7.0.0") + .WithPortBinding(27017, assignRandomHostPort: true) .WithBindMount(DockerVolumeMongoDbInit, "/docker-entrypoint-initdb.d") - .WithCommand("--replSet", "entitydb") + .WithEnvironment("MONGO_INITDB_ROOT_USERNAME", null) + .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", null) + .WithCommand("--replSet", OmniParameter) + .WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil())) .Build(); - public PostgreSqlTestcontainer PostgreSqlContainer { get; } = new TestcontainersBuilder() - .WithDatabase(_postgreSqlConfiguration) - .Build(); + public PostgreSqlContainer PostgreSqlContainer { get; } = new PostgreSqlBuilder() + .WithImage("postgres:15.4") + .WithDatabase(OmniParameter) + .WithUsername(OmniParameter) + .WithPassword(OmniParameter) + .Build(); public async Task InitializeAsync() { diff --git a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj index 47634e17..d83a4dd7 100644 --- a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj +++ b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj @@ -9,8 +9,10 @@ - - + + + + @@ -19,14 +21,4 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 0cd3f810..0561fb90 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -31,6 +31,7 @@ using MongoDB.Driver; using Moq; using Shouldly; +using Testcontainers.MongoDb; using Xunit.Abstractions; using Xunit.DependencyInjection; using Xunit.DependencyInjection.Logging; @@ -64,8 +65,11 @@ public class TestsBase serviceCollection.ConfigureAll(options => { - options.ConnectionString = databaseContainerFixture!.MongoDbContainer.ConnectionString.Replace("mongodb://:@", "mongodb://"); - options.DatabaseName = databaseContainerFixture.MongoDbConfiguration.Database; + var host = databaseContainerFixture!.MongoDbContainer.Hostname; + var port = databaseContainerFixture!.MongoDbContainer.GetMappedPublicPort(27017); + + options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); + options.DatabaseName = DatabaseContainerFixture.OmniParameter; options.WriteTimeout = TimeSpan.FromSeconds(1); }); @@ -103,7 +107,7 @@ public class TestsBase serviceCollection.ConfigureAll(options => { - options.ConnectionString = $"{databaseContainerFixture!.PostgreSqlContainer.ConnectionString};Include Error Detail=true"; + options.ConnectionString = $"{databaseContainerFixture!.PostgreSqlContainer.GetConnectionString()};Include Error Detail=true"; }); serviceCollection.Configure(TestSessionOptions.Write, options => @@ -167,7 +171,7 @@ private static SnapshotAdder EntityFrameworkSnapshotAdder() serviceCollection.ConfigureAll(options => { - options.ConnectionString = databaseContainerFixture!.PostgreSqlContainer.ConnectionString; + options.ConnectionString = databaseContainerFixture!.PostgreSqlContainer.GetConnectionString(); }); serviceCollection.Configure(TestSessionOptions.Write, options => @@ -200,7 +204,7 @@ private static SnapshotAdder RedisSnapshotAdder() serviceCollection.ConfigureAll(options => { - options.ConnectionString = databaseContainerFixture!.RedisContainer.ConnectionString; + options.ConnectionString = databaseContainerFixture!.RedisContainer.GetConnectionString(); options.KeyNamespace = TSnapshot.RedisKeyNamespace; }); @@ -271,8 +275,8 @@ from snapshotAdder in AllSnapshotAdders() select new SnapshotAdder(snapshotAdder.Name, snapshotAdder.SnapshotType, snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => { - serviceCollection.AddEntitySnapshotTransactionSubscriber(TestSessionOptions.ReadOnly, - TestSessionOptions.Write, true); + serviceCollection.AddEntitySnapshotSourceSubscriber(TestSessionOptions.ReadOnly, + TestSessionOptions.Write); })); } @@ -295,8 +299,8 @@ private static IEnumerable AllProjectionAdders() snapshotAdder.AddDependencies + (serviceCollection => { serviceCollection.AddProjection(); - serviceCollection.AddProjectionSnapshotTransactionSubscriber( - TestSessionOptions.ReadOnly, TestSessionOptions.Write, true); + serviceCollection.AddProjectionSnapshotSourceSubscriber( + TestSessionOptions.ReadOnly, TestSessionOptions.Write); })) ); } @@ -355,10 +359,11 @@ protected IServiceScope CreateServiceScope(Action? configure serviceCollection.AddSingleton(_configuration); serviceCollection.AddSingleton(_test); + serviceCollection.AddSingleton(_testOutputHelperAccessor); serviceCollection.AddLogging(loggingBuilder => { - loggingBuilder.AddProvider(new XunitTestOutputLoggerProvider(_testOutputHelperAccessor, (_,_) => true)); + loggingBuilder.AddXunitOutput(options => { options.IncludeScopes = true; }); loggingBuilder.AddDebug(); loggingBuilder.AddSimpleConsole(options => { options.IncludeScopes = true; }); }); diff --git a/test/EntityDb.Common.Tests/packages.lock.json b/test/EntityDb.Common.Tests/packages.lock.json index 48a2f446..4d67a9bb 100644 --- a/test/EntityDb.Common.Tests/packages.lock.json +++ b/test/EntityDb.Common.Tests/packages.lock.json @@ -10,18 +10,18 @@ }, "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.7.1, )", + "resolved": "17.7.1", + "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.7.1", + "Microsoft.TestPlatform.TestHost": "17.7.1" } }, "Moq": { @@ -35,25 +35,24 @@ }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "Direct", - "requested": "[7.0.0, )", - "resolved": "7.0.0", - "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "requested": "[7.0.4, )", + "resolved": "7.0.4", + "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", - "Npgsql": "7.0.0" + "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", + "Npgsql": "7.0.4" } }, "Shouldly": { "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "sEmt1Wf3VvSmCVMfS0XsmnlLubqK9dTk7RxwMxDjk0YYnkAnb3S+wESntgrjgbcszO+HzVxUy9iVJxwxT1HWIw==", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "DiffEngine": "10.0.0", - "EmptyFiles": "2.8.0", - "Microsoft.CSharp": "4.7.0" + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" } }, "System.Linq.Async": { @@ -65,58 +64,86 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "Testcontainers": { + "Testcontainers.MongoDb": { "type": "Direct", - "requested": "[2.2.0, )", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", - "dependencies": { - "Docker.DotNet": "3.125.12", - "Docker.DotNet.X509": "3.125.12", - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.Logging.Abstractions": "3.1.26", - "Portable.BouncyCastle": "1.9.0", - "SharpZipLib": "1.4.0", - "System.Text.Json": "4.7.2" + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.PostgreSql": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.Redis": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.2.0", + "xunit.assert": "2.5.0", + "xunit.core": "[2.5.0]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "requested": "[8.8.2, )", + "resolved": "8.8.2", + "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0", "xunit.extensibility.execution": "[2.4.2, 3.0.0)" } }, "Xunit.DependencyInjection.Logging": { "type": "Direct", - "requested": "[8.0.1, )", - "resolved": "8.0.1", - "contentHash": "SXsc9Cp9LrpH3F9+35CbtCv0zuBw8491t3o5rK1/TVYeuwzVVR2h60/uGRYIs2GyVcKNRG7hOH8MbjPaap1sHQ==", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", "dependencies": { - "Xunit.DependencyInjection": "8.4.1" + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } }, "Castle.Core": { "type": "Transitive", @@ -128,11 +155,11 @@ }, "DiffEngine": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "H8F7V1zRHkWLP5AW9lCxZypanXlFRT8n7P9Ou8cxz189Yg8TEw5FwTqQCaXjVPRjfE8621lhblpQrghbGJgDZw==", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", "dependencies": { - "EmptyFiles": "2.8.0", - "System.Management": "5.0.0" + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" } }, "DnsClient": { @@ -145,8 +172,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.13", + "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -155,16 +182,31 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.13", + "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.13" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2022.3.1", + "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", @@ -173,21 +215,16 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "17.7.1", + "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "resolved": "7.0.10", + "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", "Microsoft.Extensions.Caching.Memory": "7.0.0", "Microsoft.Extensions.DependencyInjection": "7.0.0", "Microsoft.Extensions.Logging": "7.0.0" @@ -195,20 +232,20 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + "resolved": "7.0.10", + "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + "resolved": "7.0.10", + "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "7.0.10", + "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.EntityFrameworkCore": "7.0.10", "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" } }, @@ -502,19 +539,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.7.1", + "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", "dependencies": { - "NuGet.Frameworks": "5.11.0", + "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.7.1", + "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.7.1", "Newtonsoft.Json": "13.0.1" } }, @@ -537,42 +574,36 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" - } - }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.21.0", + "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.21.0", + "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.21.0", + "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.21.0", + "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -581,8 +612,8 @@ }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -642,22 +673,21 @@ }, "Npgsql": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", + "resolved": "7.0.4", + "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "NuGet.Frameworks": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "2.2.2", - "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { "System.IO.Pipelines": "5.0.1" } @@ -781,8 +811,8 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" }, "Snappier": { "type": "Transitive", @@ -791,11 +821,10 @@ }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.6.122", + "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -813,8 +842,8 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" }, "System.Collections": { "type": "Transitive", @@ -856,15 +885,6 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "5.0.0", - "System.Security.Permissions": "5.0.0" - } - }, "System.Console": { "type": "Transitive", "resolved": "4.3.0", @@ -889,10 +909,14 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "resolved": "4.3.0", + "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Diagnostics.EventLog": { @@ -900,17 +924,6 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Configuration.ConfigurationManager": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "System.Diagnostics.Tools": { "type": "Transitive", "resolved": "4.3.0", @@ -931,14 +944,6 @@ "System.Runtime": "4.3.0" } }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" - } - }, "System.Globalization": { "type": "Transitive", "resolved": "4.3.0", @@ -1089,14 +1094,17 @@ }, "System.Management": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.CodeDom": "5.0.0" + "System.CodeDom": "6.0.0" } }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Net.Http": { "type": "Transitive", "resolved": "4.3.0", @@ -1451,11 +1459,6 @@ "System.Threading.Tasks": "4.3.0" } }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==" - }, "System.Security.Cryptography.X509Certificates": { "type": "Transitive", "resolved": "4.3.0", @@ -1488,15 +1491,6 @@ "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" } }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", @@ -1533,8 +1527,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "6.0.7", + "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1582,14 +1576,6 @@ "System.Runtime": "4.3.0" } }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } - }, "System.Xml.ReaderWriter": { "type": "Transitive", "resolved": "4.3.0", @@ -1631,6 +1617,20 @@ "System.Xml.ReaderWriter": "4.3.0" } }, + "Testcontainers": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", + "dependencies": { + "Docker.DotNet": "3.125.13", + "Docker.DotNet.X509": "3.125.13", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.3", + "Portable.BouncyCastle": "1.9.0", + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.7" + } + }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -1638,30 +1638,30 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.2.0", + "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", + "resolved": "2.5.0", + "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", "dependencies": { "NETStandard.Library": "1.6.1" } }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.5.0", + "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]", + "xunit.extensibility.execution": "[2.5.0]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.5.0", + "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1669,11 +1669,11 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.5.0", + "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]" } }, "ZstdSharp.Port": { @@ -1698,7 +1698,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1720,7 +1720,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.0, )", + "MongoDB.Driver": "[2.21.0, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1728,7 +1728,7 @@ "type": "Project", "dependencies": { "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", + "Npgsql": "[7.0.4, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1747,7 +1747,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", + "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } }, diff --git a/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj b/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj index debf235b..7821afba 100644 --- a/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj +++ b/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj @@ -5,14 +5,4 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/test/EntityDb.MongoDb.Tests/packages.lock.json b/test/EntityDb.MongoDb.Tests/packages.lock.json index ddbc98af..43ba1fed 100644 --- a/test/EntityDb.MongoDb.Tests/packages.lock.json +++ b/test/EntityDb.MongoDb.Tests/packages.lock.json @@ -10,18 +10,18 @@ }, "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.7.1, )", + "resolved": "17.7.1", + "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.7.1", + "Microsoft.TestPlatform.TestHost": "17.7.1" } }, "Moq": { @@ -35,13 +35,12 @@ }, "Shouldly": { "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "sEmt1Wf3VvSmCVMfS0XsmnlLubqK9dTk7RxwMxDjk0YYnkAnb3S+wESntgrjgbcszO+HzVxUy9iVJxwxT1HWIw==", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "DiffEngine": "10.0.0", - "EmptyFiles": "2.8.0", - "Microsoft.CSharp": "4.7.0" + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" } }, "System.Linq.Async": { @@ -55,41 +54,54 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.2.0", + "xunit.assert": "2.5.0", + "xunit.core": "[2.5.0]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "requested": "[8.8.2, )", + "resolved": "8.8.2", + "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0", "xunit.extensibility.execution": "[2.4.2, 3.0.0)" } }, "Xunit.DependencyInjection.Logging": { "type": "Direct", - "requested": "[8.0.1, )", - "resolved": "8.0.1", - "contentHash": "SXsc9Cp9LrpH3F9+35CbtCv0zuBw8491t3o5rK1/TVYeuwzVVR2h60/uGRYIs2GyVcKNRG7hOH8MbjPaap1sHQ==", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", "dependencies": { - "Xunit.DependencyInjection": "8.4.1" + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } }, "Castle.Core": { "type": "Transitive", @@ -101,11 +113,11 @@ }, "DiffEngine": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "H8F7V1zRHkWLP5AW9lCxZypanXlFRT8n7P9Ou8cxz189Yg8TEw5FwTqQCaXjVPRjfE8621lhblpQrghbGJgDZw==", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", "dependencies": { - "EmptyFiles": "2.8.0", - "System.Management": "5.0.0" + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" } }, "DnsClient": { @@ -118,8 +130,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.13", + "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -128,16 +140,31 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.13", + "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.13" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2022.3.1", + "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", @@ -146,21 +173,16 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "17.7.1", + "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "resolved": "7.0.10", + "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", "Microsoft.Extensions.Caching.Memory": "7.0.0", "Microsoft.Extensions.DependencyInjection": "7.0.0", "Microsoft.Extensions.Logging": "7.0.0" @@ -168,20 +190,20 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + "resolved": "7.0.10", + "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + "resolved": "7.0.10", + "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "7.0.10", + "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.EntityFrameworkCore": "7.0.10", "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" } }, @@ -475,19 +497,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.7.1", + "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", "dependencies": { - "NuGet.Frameworks": "5.11.0", + "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.7.1", + "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.7.1", "Newtonsoft.Json": "13.0.1" } }, @@ -510,42 +532,36 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" - } - }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.21.0", + "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.21.0", + "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.21.0", + "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.21.0", + "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -554,8 +570,8 @@ }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -615,33 +631,32 @@ }, "Npgsql": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", + "resolved": "7.0.4", + "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "resolved": "7.0.4", + "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", - "Npgsql": "7.0.0" + "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", + "Npgsql": "7.0.4" } }, "NuGet.Frameworks": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "2.2.2", - "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { "System.IO.Pipelines": "5.0.1" } @@ -765,8 +780,8 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" }, "Snappier": { "type": "Transitive", @@ -775,11 +790,10 @@ }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.6.122", + "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -797,8 +811,8 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" }, "System.Collections": { "type": "Transitive", @@ -840,15 +854,6 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "5.0.0", - "System.Security.Permissions": "5.0.0" - } - }, "System.Console": { "type": "Transitive", "resolved": "4.3.0", @@ -873,10 +878,14 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "resolved": "4.3.0", + "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Diagnostics.EventLog": { @@ -884,17 +893,6 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Configuration.ConfigurationManager": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "System.Diagnostics.Tools": { "type": "Transitive", "resolved": "4.3.0", @@ -915,14 +913,6 @@ "System.Runtime": "4.3.0" } }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" - } - }, "System.Globalization": { "type": "Transitive", "resolved": "4.3.0", @@ -1073,14 +1063,17 @@ }, "System.Management": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.CodeDom": "5.0.0" + "System.CodeDom": "6.0.0" } }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Net.Http": { "type": "Transitive", "resolved": "4.3.0", @@ -1435,11 +1428,6 @@ "System.Threading.Tasks": "4.3.0" } }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==" - }, "System.Security.Cryptography.X509Certificates": { "type": "Transitive", "resolved": "4.3.0", @@ -1472,15 +1460,6 @@ "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" } }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", @@ -1517,8 +1496,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "6.0.7", + "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1566,14 +1545,6 @@ "System.Runtime": "4.3.0" } }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } - }, "System.Xml.ReaderWriter": { "type": "Transitive", "resolved": "4.3.0", @@ -1617,16 +1588,43 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "resolved": "3.0.0", + "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", "dependencies": { - "Docker.DotNet": "3.125.12", - "Docker.DotNet.X509": "3.125.12", - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.Logging.Abstractions": "3.1.26", + "Docker.DotNet": "3.125.13", + "Docker.DotNet.X509": "3.125.13", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.3", "Portable.BouncyCastle": "1.9.0", - "SharpZipLib": "1.4.0", - "System.Text.Json": "4.7.2" + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.7" + } + }, + "Testcontainers.MongoDb": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.PostgreSql": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.Redis": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" } }, "xunit.abstractions": { @@ -1636,30 +1634,30 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.2.0", + "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", + "resolved": "2.5.0", + "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", "dependencies": { "NETStandard.Library": "1.6.1" } }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.5.0", + "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]", + "xunit.extensibility.execution": "[2.5.0]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.5.0", + "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1667,11 +1665,11 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.5.0", + "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]" } }, "ZstdSharp.Port": { @@ -1701,22 +1699,24 @@ "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", - "Microsoft.NET.Test.Sdk": "[17.4.0, )", + "Microsoft.NET.Test.Sdk": "[17.7.1, )", "Moq": "[4.18.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.0, )", - "Shouldly": "[4.1.0, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.4, )", + "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", - "Testcontainers": "[2.2.0, )", - "Xunit.DependencyInjection": "[8.6.1, )", - "Xunit.DependencyInjection.Logging": "[8.0.1, )", - "xunit": "[2.4.2, )" + "Testcontainers.MongoDb": "[3.0.0, )", + "Testcontainers.PostgreSql": "[3.0.0, )", + "Testcontainers.Redis": "[3.0.0, )", + "Xunit.DependencyInjection": "[8.8.2, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.5.0, )" } }, "entitydb.entityframework": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1738,7 +1738,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.0, )", + "MongoDB.Driver": "[2.21.0, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1746,7 +1746,7 @@ "type": "Project", "dependencies": { "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", + "Npgsql": "[7.0.4, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1765,7 +1765,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", + "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } }, diff --git a/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj b/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj index cf8c57f4..556f9cf3 100644 --- a/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj +++ b/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj @@ -5,14 +5,4 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/test/EntityDb.Mvc.Tests/packages.lock.json b/test/EntityDb.Mvc.Tests/packages.lock.json index 866d07aa..9f4090b5 100644 --- a/test/EntityDb.Mvc.Tests/packages.lock.json +++ b/test/EntityDb.Mvc.Tests/packages.lock.json @@ -10,18 +10,18 @@ }, "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.7.1, )", + "resolved": "17.7.1", + "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.7.1", + "Microsoft.TestPlatform.TestHost": "17.7.1" } }, "Moq": { @@ -35,13 +35,12 @@ }, "Shouldly": { "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "sEmt1Wf3VvSmCVMfS0XsmnlLubqK9dTk7RxwMxDjk0YYnkAnb3S+wESntgrjgbcszO+HzVxUy9iVJxwxT1HWIw==", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "DiffEngine": "10.0.0", - "EmptyFiles": "2.8.0", - "Microsoft.CSharp": "4.7.0" + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" } }, "System.Linq.Async": { @@ -55,41 +54,54 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.2.0", + "xunit.assert": "2.5.0", + "xunit.core": "[2.5.0]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "requested": "[8.8.2, )", + "resolved": "8.8.2", + "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0", "xunit.extensibility.execution": "[2.4.2, 3.0.0)" } }, "Xunit.DependencyInjection.Logging": { "type": "Direct", - "requested": "[8.0.1, )", - "resolved": "8.0.1", - "contentHash": "SXsc9Cp9LrpH3F9+35CbtCv0zuBw8491t3o5rK1/TVYeuwzVVR2h60/uGRYIs2GyVcKNRG7hOH8MbjPaap1sHQ==", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", "dependencies": { - "Xunit.DependencyInjection": "8.4.1" + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } }, "Castle.Core": { "type": "Transitive", @@ -101,11 +113,11 @@ }, "DiffEngine": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "H8F7V1zRHkWLP5AW9lCxZypanXlFRT8n7P9Ou8cxz189Yg8TEw5FwTqQCaXjVPRjfE8621lhblpQrghbGJgDZw==", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", "dependencies": { - "EmptyFiles": "2.8.0", - "System.Management": "5.0.0" + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" } }, "DnsClient": { @@ -118,8 +130,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.13", + "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -128,16 +140,31 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.13", + "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.13" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2022.3.1", + "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", @@ -146,21 +173,16 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "17.7.1", + "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "resolved": "7.0.10", + "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", "Microsoft.Extensions.Caching.Memory": "7.0.0", "Microsoft.Extensions.DependencyInjection": "7.0.0", "Microsoft.Extensions.Logging": "7.0.0" @@ -168,20 +190,20 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + "resolved": "7.0.10", + "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + "resolved": "7.0.10", + "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "7.0.10", + "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.EntityFrameworkCore": "7.0.10", "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" } }, @@ -475,19 +497,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.7.1", + "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", "dependencies": { - "NuGet.Frameworks": "5.11.0", + "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.7.1", + "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.7.1", "Newtonsoft.Json": "13.0.1" } }, @@ -510,42 +532,36 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" - } - }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.21.0", + "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.21.0", + "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.21.0", + "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.21.0", + "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -554,8 +570,8 @@ }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -615,33 +631,32 @@ }, "Npgsql": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", + "resolved": "7.0.4", + "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "resolved": "7.0.4", + "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", - "Npgsql": "7.0.0" + "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", + "Npgsql": "7.0.4" } }, "NuGet.Frameworks": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "2.2.2", - "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { "System.IO.Pipelines": "5.0.1" } @@ -765,8 +780,8 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" }, "Snappier": { "type": "Transitive", @@ -775,11 +790,10 @@ }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.6.122", + "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -797,8 +811,8 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" }, "System.Collections": { "type": "Transitive", @@ -840,15 +854,6 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "5.0.0", - "System.Security.Permissions": "5.0.0" - } - }, "System.Console": { "type": "Transitive", "resolved": "4.3.0", @@ -873,10 +878,14 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "resolved": "4.3.0", + "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Diagnostics.EventLog": { @@ -884,17 +893,6 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Configuration.ConfigurationManager": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "System.Diagnostics.Tools": { "type": "Transitive", "resolved": "4.3.0", @@ -915,14 +913,6 @@ "System.Runtime": "4.3.0" } }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" - } - }, "System.Globalization": { "type": "Transitive", "resolved": "4.3.0", @@ -1073,14 +1063,17 @@ }, "System.Management": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.CodeDom": "5.0.0" + "System.CodeDom": "6.0.0" } }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Net.Http": { "type": "Transitive", "resolved": "4.3.0", @@ -1435,11 +1428,6 @@ "System.Threading.Tasks": "4.3.0" } }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==" - }, "System.Security.Cryptography.X509Certificates": { "type": "Transitive", "resolved": "4.3.0", @@ -1472,15 +1460,6 @@ "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" } }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", @@ -1517,8 +1496,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "6.0.7", + "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1566,14 +1545,6 @@ "System.Runtime": "4.3.0" } }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } - }, "System.Xml.ReaderWriter": { "type": "Transitive", "resolved": "4.3.0", @@ -1617,16 +1588,43 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "resolved": "3.0.0", + "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", "dependencies": { - "Docker.DotNet": "3.125.12", - "Docker.DotNet.X509": "3.125.12", - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.Logging.Abstractions": "3.1.26", + "Docker.DotNet": "3.125.13", + "Docker.DotNet.X509": "3.125.13", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.3", "Portable.BouncyCastle": "1.9.0", - "SharpZipLib": "1.4.0", - "System.Text.Json": "4.7.2" + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.7" + } + }, + "Testcontainers.MongoDb": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.PostgreSql": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.Redis": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" } }, "xunit.abstractions": { @@ -1636,30 +1634,30 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.2.0", + "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", + "resolved": "2.5.0", + "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", "dependencies": { "NETStandard.Library": "1.6.1" } }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.5.0", + "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]", + "xunit.extensibility.execution": "[2.5.0]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.5.0", + "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1667,11 +1665,11 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.5.0", + "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]" } }, "ZstdSharp.Port": { @@ -1701,22 +1699,24 @@ "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", - "Microsoft.NET.Test.Sdk": "[17.4.0, )", + "Microsoft.NET.Test.Sdk": "[17.7.1, )", "Moq": "[4.18.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.0, )", - "Shouldly": "[4.1.0, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.4, )", + "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", - "Testcontainers": "[2.2.0, )", - "Xunit.DependencyInjection": "[8.6.1, )", - "Xunit.DependencyInjection.Logging": "[8.0.1, )", - "xunit": "[2.4.2, )" + "Testcontainers.MongoDb": "[3.0.0, )", + "Testcontainers.PostgreSql": "[3.0.0, )", + "Testcontainers.Redis": "[3.0.0, )", + "Xunit.DependencyInjection": "[8.8.2, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.5.0, )" } }, "entitydb.entityframework": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1738,7 +1738,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.0, )", + "MongoDB.Driver": "[2.21.0, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1753,7 +1753,7 @@ "type": "Project", "dependencies": { "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", + "Npgsql": "[7.0.4, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1772,7 +1772,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", + "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } }, diff --git a/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj b/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj index b4c9f0de..4bb94292 100644 --- a/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj +++ b/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj @@ -5,14 +5,4 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/test/EntityDb.Redis.Tests/packages.lock.json b/test/EntityDb.Redis.Tests/packages.lock.json index ddbc98af..43ba1fed 100644 --- a/test/EntityDb.Redis.Tests/packages.lock.json +++ b/test/EntityDb.Redis.Tests/packages.lock.json @@ -10,18 +10,18 @@ }, "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.7.1, )", + "resolved": "17.7.1", + "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.7.1", + "Microsoft.TestPlatform.TestHost": "17.7.1" } }, "Moq": { @@ -35,13 +35,12 @@ }, "Shouldly": { "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "sEmt1Wf3VvSmCVMfS0XsmnlLubqK9dTk7RxwMxDjk0YYnkAnb3S+wESntgrjgbcszO+HzVxUy9iVJxwxT1HWIw==", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "DiffEngine": "10.0.0", - "EmptyFiles": "2.8.0", - "Microsoft.CSharp": "4.7.0" + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" } }, "System.Linq.Async": { @@ -55,41 +54,54 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.2.0", + "xunit.assert": "2.5.0", + "xunit.core": "[2.5.0]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "requested": "[8.8.2, )", + "resolved": "8.8.2", + "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0", "xunit.extensibility.execution": "[2.4.2, 3.0.0)" } }, "Xunit.DependencyInjection.Logging": { "type": "Direct", - "requested": "[8.0.1, )", - "resolved": "8.0.1", - "contentHash": "SXsc9Cp9LrpH3F9+35CbtCv0zuBw8491t3o5rK1/TVYeuwzVVR2h60/uGRYIs2GyVcKNRG7hOH8MbjPaap1sHQ==", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", "dependencies": { - "Xunit.DependencyInjection": "8.4.1" + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.5.0, )", + "resolved": "2.5.0", + "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } }, "Castle.Core": { "type": "Transitive", @@ -101,11 +113,11 @@ }, "DiffEngine": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "H8F7V1zRHkWLP5AW9lCxZypanXlFRT8n7P9Ou8cxz189Yg8TEw5FwTqQCaXjVPRjfE8621lhblpQrghbGJgDZw==", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", "dependencies": { - "EmptyFiles": "2.8.0", - "System.Management": "5.0.0" + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" } }, "DnsClient": { @@ -118,8 +130,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.13", + "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -128,16 +140,31 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.13", + "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.13" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2022.3.1", + "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", @@ -146,21 +173,16 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "17.7.1", + "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "9W+IfmAzMrp2ZpKZLhgTlWljSBM9Erldis1us61DAGi+L7Q6vilTbe1G2zDxtYO8F2H0I0Qnupdx5Cp4s2xoZw==", + "resolved": "7.0.10", + "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.0", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.0", + "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", + "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", "Microsoft.Extensions.Caching.Memory": "7.0.0", "Microsoft.Extensions.DependencyInjection": "7.0.0", "Microsoft.Extensions.Logging": "7.0.0" @@ -168,20 +190,20 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Pfu3Zjj5+d2Gt27oE9dpGiF/VobBB+s5ogrfI9sBsXQE1SG49RqVz5+IyeNnzhyejFrPIQsPDRMchhcojy4Hbw==" + "resolved": "7.0.10", + "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Qkd2H+jLe37o5ku+LjT6qf7kAHY75Yfn2bBDQgqr13DTOLYpEy1Mt93KPFjaZvIu/srEcbfGGMRL7urKm5zN8Q==" + "resolved": "7.0.10", + "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "7.0.10", + "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", + "Microsoft.EntityFrameworkCore": "7.0.10", "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" } }, @@ -475,19 +497,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.7.1", + "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", "dependencies": { - "NuGet.Frameworks": "5.11.0", + "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.7.1", + "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.7.1", "Newtonsoft.Json": "13.0.1" } }, @@ -510,42 +532,36 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" - } - }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.21.0", + "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.21.0", + "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.21.0", + "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.21.0", + "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.21.0", + "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -554,8 +570,8 @@ }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -615,33 +631,32 @@ }, "Npgsql": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", + "resolved": "7.0.4", + "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "resolved": "7.0.4", + "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.0, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, 8.0.0)", - "Npgsql": "7.0.0" + "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", + "Npgsql": "7.0.4" } }, "NuGet.Frameworks": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "2.2.2", - "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { "System.IO.Pipelines": "5.0.1" } @@ -765,8 +780,8 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" }, "Snappier": { "type": "Transitive", @@ -775,11 +790,10 @@ }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.6.122", + "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -797,8 +811,8 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" }, "System.Collections": { "type": "Transitive", @@ -840,15 +854,6 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "5.0.0", - "System.Security.Permissions": "5.0.0" - } - }, "System.Console": { "type": "Transitive", "resolved": "4.3.0", @@ -873,10 +878,14 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "resolved": "4.3.0", + "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Diagnostics.EventLog": { @@ -884,17 +893,6 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.Configuration.ConfigurationManager": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "System.Diagnostics.Tools": { "type": "Transitive", "resolved": "4.3.0", @@ -915,14 +913,6 @@ "System.Runtime": "4.3.0" } }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" - } - }, "System.Globalization": { "type": "Transitive", "resolved": "4.3.0", @@ -1073,14 +1063,17 @@ }, "System.Management": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "Microsoft.Win32.Registry": "5.0.0", - "System.CodeDom": "5.0.0" + "System.CodeDom": "6.0.0" } }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Net.Http": { "type": "Transitive", "resolved": "4.3.0", @@ -1435,11 +1428,6 @@ "System.Threading.Tasks": "4.3.0" } }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==" - }, "System.Security.Cryptography.X509Certificates": { "type": "Transitive", "resolved": "4.3.0", @@ -1472,15 +1460,6 @@ "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" } }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", @@ -1517,8 +1496,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "6.0.7", + "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1566,14 +1545,6 @@ "System.Runtime": "4.3.0" } }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } - }, "System.Xml.ReaderWriter": { "type": "Transitive", "resolved": "4.3.0", @@ -1617,16 +1588,43 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "resolved": "3.0.0", + "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", "dependencies": { - "Docker.DotNet": "3.125.12", - "Docker.DotNet.X509": "3.125.12", - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.Logging.Abstractions": "3.1.26", + "Docker.DotNet": "3.125.13", + "Docker.DotNet.X509": "3.125.13", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.3", "Portable.BouncyCastle": "1.9.0", - "SharpZipLib": "1.4.0", - "System.Text.Json": "4.7.2" + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.7" + } + }, + "Testcontainers.MongoDb": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.PostgreSql": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" + } + }, + "Testcontainers.Redis": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "dependencies": { + "JetBrains.Annotations": "2022.3.1", + "Testcontainers": "3.0.0" } }, "xunit.abstractions": { @@ -1636,30 +1634,30 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.2.0", + "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", + "resolved": "2.5.0", + "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", "dependencies": { "NETStandard.Library": "1.6.1" } }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.5.0", + "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]", + "xunit.extensibility.execution": "[2.5.0]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.5.0", + "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1667,11 +1665,11 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.5.0", + "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.5.0]" } }, "ZstdSharp.Port": { @@ -1701,22 +1699,24 @@ "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", - "Microsoft.NET.Test.Sdk": "[17.4.0, )", + "Microsoft.NET.Test.Sdk": "[17.7.1, )", "Moq": "[4.18.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.0, )", - "Shouldly": "[4.1.0, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.4, )", + "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", - "Testcontainers": "[2.2.0, )", - "Xunit.DependencyInjection": "[8.6.1, )", - "Xunit.DependencyInjection.Logging": "[8.0.1, )", - "xunit": "[2.4.2, )" + "Testcontainers.MongoDb": "[3.0.0, )", + "Testcontainers.PostgreSql": "[3.0.0, )", + "Testcontainers.Redis": "[3.0.0, )", + "Xunit.DependencyInjection": "[8.8.2, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.5.0, )" } }, "entitydb.entityframework": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1738,7 +1738,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.0, )", + "MongoDB.Driver": "[2.21.0, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1746,7 +1746,7 @@ "type": "Project", "dependencies": { "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", + "Npgsql": "[7.0.4, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1765,7 +1765,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", + "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } }, From 3f3950623872666bf6252a838f09ebc8edfc8f51 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:31:14 -0700 Subject: [PATCH 41/93] refactor: generalize source --- .../IAgentSignatureFilterBuilder.cs | 17 ++++--- .../Queries/FilterBuilders/IFilterBuilder.cs | 51 +++++++++++-------- src/EntityDb.Abstractions/Sources/ISource.cs | 30 +++++++++++ .../Sources/ISourceRepository.cs | 14 +++++ .../Sources/ISourceSubscriber.cs | 13 +++++ .../Transactions/ITransaction.cs | 24 ++------- .../Transactions/ITransactionCommand.cs | 8 ++- .../Queries/GetAgentSignatures.cs | 2 +- .../Builders/TransactionBuilder.cs | 8 +-- .../Transactions/TransactionCommand.cs | 2 +- .../TransactionCommandWithSnapshot.cs | 6 --- .../Documents/CommandDocument.cs | 4 +- .../AgentSignatureFilterBuilder.cs | 4 +- .../FilterBuilders/FilterBuilderBase.cs | 6 +-- .../MongoDbTransactionRepository.cs | 8 +-- .../Documents/Command/CommandDocument.cs | 4 +- .../AgentSignatureFilterBuilder.cs | 4 +- .../FilterBuilders/FilterBuilderBase.cs | 6 +-- .../SqlDbTransactionRepository.cs | 8 +-- .../Entities/EntityTests.cs | 2 +- .../Implementations/Queries/EntityIdQuery.cs | 10 ++-- .../Queries/TransactionIdQuery.cs | 8 +-- .../Queries/TransactionTimeStampQuery.cs | 16 +++--- .../SingleEntityTransactionBuilderTests.cs | 4 +- 24 files changed, 154 insertions(+), 105 deletions(-) create mode 100644 src/EntityDb.Abstractions/Sources/ISource.cs create mode 100644 src/EntityDb.Abstractions/Sources/ISourceRepository.cs create mode 100644 src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs delete mode 100644 src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs index 7744a93b..718f6199 100644 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs @@ -8,17 +8,20 @@ namespace EntityDb.Abstractions.Queries.FilterBuilders; /// The type of filter used by the repository. public interface IAgentSignatureFilterBuilder : IFilterBuilder { + /// + [Obsolete("Please use SubjectIdsIn instead. This will be removed in a future version.")] + TFilter EntityIdsIn(params Id[] entityIds) => SubjectIdsIn(entityIds); + /// - /// Returns a that only includes agentSignatures with any entity id which is contained - /// in a set - /// of entity ids. + /// Returns a that only includes agentSignatures with any source id which is contained + /// in a set of source ids. /// - /// The set of entity ids. + /// The set of subject ids. /// - /// A that only includes agentSignatures with any entity id which is contained in - /// . + /// A that only includes agentSignatures with any source id which is contained in + /// . /// - TFilter EntityIdsIn(params Id[] entityIds); + TFilter SubjectIdsIn(params Id[] subjectIds); /// /// Returns a that only includes agentSignatures whose type is contained in a set of diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs index aa797265..9cebb2fc 100644 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs @@ -4,44 +4,55 @@ namespace EntityDb.Abstractions.Queries.FilterBuilders; /// /// Builds a for an object repository. Possible objects include: agentSignatures, -/// commands, -/// facts, and leases. +/// commands, facts, tags, and events. /// /// The type of filter used by the repository. public interface IFilterBuilder { + /// + [Obsolete("Please use SourceTimeStampGte instead. This will be removed in a future version.")] + TFilter TransactionTimeStampGte(TimeStamp transactionTimeStamp) => SourceTimeStampGte(transactionTimeStamp); + + /// + [Obsolete("Please use SourceTimeStampLte instead. This will be removed in a future version.")] + TFilter TransactionTimeStampLte(TimeStamp transactionTimeStamp) => SourceTimeStampLte(transactionTimeStamp); + + /// + [Obsolete("Please use SourceIdIn instead. This will be removed in a future version.")] + TFilter TransactionIdIn(params Id[] transactionIds) => SourceIdIn(transactionIds); + /// - /// Returns a that only includes objects with a transaction timestamp greater than or - /// equal to a transaction timestamp. + /// Returns a that only includes objects with a source timestamp greater than or + /// equal to a source timestamp. /// - /// The transaction timestamp. + /// The source timestamp. /// - /// A that only includes objects with an transaction timestamp greater than or - /// equal to . + /// A that only includes objects with an source timestamp greater than or + /// equal to . /// - TFilter TransactionTimeStampGte(TimeStamp transactionTimeStamp); + TFilter SourceTimeStampGte(TimeStamp sourceTimeStamp); /// - /// Returns a that only includes objects with a transaction timestamp less than or - /// equal to a transaction timestamp. + /// Returns a that only includes objects with a source timestamp less than or + /// equal to a source timestamp. /// - /// The transaction timestamp. + /// The source timestamp. /// - /// A that only includes objects with an transaction timestamp less than or equal - /// to . + /// A that only includes objects with an source timestamp less than or equal + /// to . /// - TFilter TransactionTimeStampLte(TimeStamp transactionTimeStamp); + TFilter SourceTimeStampLte(TimeStamp sourceTimeStamp); /// - /// Returns a that only includes objects with an transaction id which is contained in a - /// set of transaction ids. + /// Returns a that only includes objects with an source id which is contained in a + /// set of source ids. /// - /// The set of transaction ids. + /// The set of source ids. /// - /// A that only includes objects with an transaction id which is contained in - /// . + /// A that only includes objects with an source id which is contained in + /// . /// - TFilter TransactionIdIn(params Id[] transactionIds); + TFilter SourceIdIn(params Id[] sourceIds); /// /// Returns a that excludes objects which do match a filter. diff --git a/src/EntityDb.Abstractions/Sources/ISource.cs b/src/EntityDb.Abstractions/Sources/ISource.cs new file mode 100644 index 00000000..99d9de18 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/ISource.cs @@ -0,0 +1,30 @@ +using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Sources; + +/// +/// Represents a generic piece of information. +/// +/// +/// For now, only implements +/// this, but once events are added, ICommunication +/// will also implement this. +/// +public interface ISource +{ + /// + /// The id associated with the source. + /// + Id Id { get; } + + /// + /// The date and time associated with the source. + /// + TimeStamp TimeStamp { get; } + + /// + /// The signature of the source agent. + /// + object AgentSignature { get; } +} diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs new file mode 100644 index 00000000..bfc53d51 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -0,0 +1,14 @@ +using EntityDb.Abstractions.Transactions; + +namespace EntityDb.Abstractions.Sources; + +/// +/// Represents sources of information. +/// +public interface ISourceRepository +{ + /// + /// The backing transaction repository. + /// + ITransactionRepository TransactionRepository { get; } +} diff --git a/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs b/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs new file mode 100644 index 00000000..673bc35a --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs @@ -0,0 +1,13 @@ +namespace EntityDb.Abstractions.Sources; + +/// +/// Represents a type that reacts to sources that have been committed. +/// +public interface ISourceSubscriber +{ + /// + /// Called when a source has been committed. + /// + /// The committed source. + void Notify(ISource source); +} diff --git a/src/EntityDb.Abstractions/Transactions/ITransaction.cs b/src/EntityDb.Abstractions/Transactions/ITransaction.cs index 7fb08f4b..a6d501a3 100644 --- a/src/EntityDb.Abstractions/Transactions/ITransaction.cs +++ b/src/EntityDb.Abstractions/Transactions/ITransaction.cs @@ -1,32 +1,16 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; using System.Collections.Immutable; namespace EntityDb.Abstractions.Transactions; /// /// Represents a set of objects which must be committed together or not at all. Possible objects include: -/// agentSignatures, -/// commands, leases, and tags. +/// agentSignatures, commands, leases, and tags. /// -public interface ITransaction +public interface ITransaction : ISource { /// - /// The id associated with the set of objects. - /// - Id Id { get; } - - /// - /// The date and time associated with the set of objects. - /// - TimeStamp TimeStamp { get; } - - /// - /// The signature of the agent who requested this transaction. - /// - object AgentSignature { get; } - - /// - /// A series of sets of modifiers for a set of entities. + /// A series commands used to modify an entity. /// ImmutableArray Commands { get; } } diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs index 41ec2de9..eb82ba3e 100644 --- a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs +++ b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs @@ -18,7 +18,11 @@ public interface ITransactionCommand VersionNumber EntityVersionNumber { get; } /// - /// The command that needs to be appended. + /// The command data. /// - object Command { get; } + object Data { get; } + + /// + [Obsolete("Please use Data instead. This will be removed in a future version.")] + object Command => Data; } diff --git a/src/EntityDb.Common/Queries/GetAgentSignatures.cs b/src/EntityDb.Common/Queries/GetAgentSignatures.cs index 7eeae27b..d16dc9ef 100644 --- a/src/EntityDb.Common/Queries/GetAgentSignatures.cs +++ b/src/EntityDb.Common/Queries/GetAgentSignatures.cs @@ -9,7 +9,7 @@ internal sealed record GetAgentSignatures(Id[] TransactionIds) : IAgentSignature { public TFilter GetFilter(IAgentSignatureFilterBuilder builder) { - return builder.TransactionIdIn(TransactionIds); + return builder.SourceIdIn(TransactionIds); } public TSort? GetSort(IAgentSignatureSortBuilder builder) diff --git a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs index f1fe7db6..ab604e22 100644 --- a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs +++ b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs @@ -1,10 +1,7 @@ using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Builders; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Abstractions.Commands; using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; using System.Collections.Immutable; @@ -52,12 +49,11 @@ public ITransactionBuilder Append(Id entityId, object command) var entity = _knownEntities[entityId].Reduce(command); var entityVersionNumber = entity.GetVersionNumber(); - _transactionCommands.Add(new TransactionCommandWithSnapshot + _transactionCommands.Add(new TransactionCommand { EntityId = entityId, - Snapshot = entity, EntityVersionNumber = entityVersionNumber, - Command = command, + Data = command, }); _knownEntities[entityId] = entity; diff --git a/src/EntityDb.Common/Transactions/TransactionCommand.cs b/src/EntityDb.Common/Transactions/TransactionCommand.cs index 3656ce1c..fb50eaa2 100644 --- a/src/EntityDb.Common/Transactions/TransactionCommand.cs +++ b/src/EntityDb.Common/Transactions/TransactionCommand.cs @@ -7,5 +7,5 @@ internal record TransactionCommand : ITransactionCommand { public Id EntityId { get; init; } public VersionNumber EntityVersionNumber { get; init; } - public object Command { get; init; } = default!; + public object Data { get; init; } = default!; } diff --git a/src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs b/src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs deleted file mode 100644 index ce67e95b..00000000 --- a/src/EntityDb.Common/Transactions/TransactionCommandWithSnapshot.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace EntityDb.Common.Transactions; - -internal record TransactionCommandWithSnapshot : TransactionCommand, ITransactionCommandWithSnapshot -{ - public object Snapshot { get; init; } = default!; -} diff --git a/src/EntityDb.MongoDb/Documents/CommandDocument.cs b/src/EntityDb.MongoDb/Documents/CommandDocument.cs index 076992f2..3652cd05 100644 --- a/src/EntityDb.MongoDb/Documents/CommandDocument.cs +++ b/src/EntityDb.MongoDb/Documents/CommandDocument.cs @@ -39,8 +39,8 @@ ITransactionCommand transactionCommand TransactionId = transaction.Id, EntityId = transactionCommand.EntityId, EntityVersionNumber = transactionCommand.EntityVersionNumber, - DataType = transactionCommand.Command.GetType().Name, - Data = envelopeService.Serialize(transactionCommand.Command) + DataType = transactionCommand.Data.GetType().Name, + Data = envelopeService.Serialize(transactionCommand.Data) } }; diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs index 1f3aa150..e535dee0 100644 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs @@ -9,9 +9,9 @@ namespace EntityDb.MongoDb.Queries.FilterBuilders; internal sealed class AgentSignatureFilterBuilder : FilterBuilderBase, IAgentSignatureFilterBuilder> { - public FilterDefinition EntityIdsIn(params Id[] entityIds) + public FilterDefinition SubjectIdsIn(params Id[] subjectIds) { - return AnyIn(nameof(AgentSignatureDocument.EntityIds), entityIds); + return AnyIn(nameof(AgentSignatureDocument.EntityIds), subjectIds); } public FilterDefinition AgentSignatureTypeIn(params Type[] agentSignatureTypes) diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs index aa694f53..8790b051 100644 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs +++ b/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs @@ -11,17 +11,17 @@ internal abstract class FilterBuilderBase : BuilderBase, IFilterBuilder FilterBuilder = Builders.Filter; - public FilterDefinition TransactionTimeStampGte(TimeStamp timeStamp) + public FilterDefinition SourceTimeStampGte(TimeStamp timeStamp) { return Gte(nameof(DocumentBase.TransactionTimeStamp), timeStamp); } - public FilterDefinition TransactionTimeStampLte(TimeStamp timeStamp) + public FilterDefinition SourceTimeStampLte(TimeStamp timeStamp) { return Lte(nameof(DocumentBase.TransactionTimeStamp), timeStamp); } - public FilterDefinition TransactionIdIn(params Id[] transactionIds) + public FilterDefinition SourceIdIn(params Id[] transactionIds) { return In(nameof(DocumentBase.TransactionId), transactionIds); } diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs index 5f3b7ffd..aa6ace76 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs @@ -198,28 +198,28 @@ await CommandDocument .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_mongoSession, cancellationToken); - if (transactionCommand.Command is IAddLeasesCommand addLeasesCommand) + if (transactionCommand.Data is IAddLeasesCommand addLeasesCommand) { await LeaseDocument .GetInsertCommand(_envelopeService, transaction, transactionCommand, addLeasesCommand) .Execute(_mongoSession, cancellationToken); } - if (transactionCommand.Command is IAddTagsCommand addTagsCommand) + if (transactionCommand.Data is IAddTagsCommand addTagsCommand) { await TagDocument .GetInsertCommand(_envelopeService, transaction, transactionCommand, addTagsCommand) .Execute(_mongoSession, cancellationToken); } - if (transactionCommand.Command is IDeleteLeasesCommand deleteLeasesCommand) + if (transactionCommand.Data is IDeleteLeasesCommand deleteLeasesCommand) { await LeaseDocument .GetDeleteCommand(transactionCommand, deleteLeasesCommand) .Execute(_mongoSession, cancellationToken); } - if (transactionCommand.Command is IDeleteTagsCommand deleteTagsCommand) + if (transactionCommand.Data is IDeleteTagsCommand deleteTagsCommand) { await TagDocument .GetDeleteCommand(transactionCommand, deleteTagsCommand) diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs index bf19aab1..a988bd86 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs @@ -50,8 +50,8 @@ ITransactionCommand transactionCommand TransactionId = transaction.Id, EntityId = transactionCommand.EntityId, EntityVersionNumber = transactionCommand.EntityVersionNumber, - DataType = transactionCommand.Command.GetType().Name, - Data = envelopeService.Serialize(transactionCommand.Command) + DataType = transactionCommand.Data.GetType().Name, + Data = envelopeService.Serialize(transactionCommand.Data) } } ); diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs index d392d611..a26111fe 100644 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs +++ b/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs @@ -7,9 +7,9 @@ namespace EntityDb.SqlDb.Queries.FilterBuilders; internal sealed class AgentSignatureFilterBuilder : FilterBuilderBase, IAgentSignatureFilterBuilder { - public IFilterDefinition EntityIdsIn(params Id[] entityIds) + public IFilterDefinition SubjectIdsIn(params Id[] subjectIds) { - return AnyIn(nameof(AgentSignatureDocument.EntityIds), entityIds); + return AnyIn(nameof(AgentSignatureDocument.EntityIds), subjectIds); } public IFilterDefinition AgentSignatureTypeIn(params Type[] agentSignatureTypes) diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs index ec587dfd..7e59cd24 100644 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs +++ b/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs @@ -8,17 +8,17 @@ namespace EntityDb.SqlDb.Queries.FilterBuilders; internal abstract class FilterBuilderBase : IFilterBuilder { - public IFilterDefinition TransactionTimeStampGte(TimeStamp timeStamp) + public IFilterDefinition SourceTimeStampGte(TimeStamp timeStamp) { return Gte(nameof(ITransactionDocument.TransactionTimeStamp), timeStamp); } - public IFilterDefinition TransactionTimeStampLte(TimeStamp timeStamp) + public IFilterDefinition SourceTimeStampLte(TimeStamp timeStamp) { return Lte(nameof(ITransactionDocument.TransactionTimeStamp), timeStamp); } - public IFilterDefinition TransactionIdIn(params Id[] transactionIds) + public IFilterDefinition SourceIdIn(params Id[] transactionIds) { return In(nameof(ITransactionDocument.TransactionId), transactionIds); } diff --git a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs index d1f62478..7b9a9ac9 100644 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs +++ b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs @@ -200,28 +200,28 @@ await CommandDocument .Execute(_sqlDbSession, cancellationToken); - if (transactionCommand.Command is IAddLeasesCommand addLeasesCommand) + if (transactionCommand.Data is IAddLeasesCommand addLeasesCommand) { await LeaseDocument .GetInsertCommand(_envelopeService, transaction, transactionCommand, addLeasesCommand) .Execute(_sqlDbSession, cancellationToken); } - if (transactionCommand.Command is IAddTagsCommand addTagsCommand) + if (transactionCommand.Data is IAddTagsCommand addTagsCommand) { await TagDocument .GetInsertCommand(_envelopeService, transaction, transactionCommand, addTagsCommand) .Execute(_sqlDbSession, cancellationToken); } - if (transactionCommand.Command is IDeleteLeasesCommand deleteLeasesCommand) + if (transactionCommand.Data is IDeleteLeasesCommand deleteLeasesCommand) { await LeaseDocument .GetDeleteCommand(transactionCommand, deleteLeasesCommand) .Execute(_sqlDbSession, cancellationToken); } - if (transactionCommand.Command is IDeleteTagsCommand deleteTagsCommand) + if (transactionCommand.Data is IDeleteTagsCommand deleteTagsCommand) { await TagDocument .GetDeleteCommand(transactionCommand, deleteTagsCommand) diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 141b40b9..e4125c6b 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -114,7 +114,7 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T { foreach (var transactionCommand in transaction.Commands) { - commands.Add(transactionCommand.Command); + commands.Add(transactionCommand.Data); } return true; diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs index 36fe9117..1db26fad 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs @@ -9,7 +9,7 @@ public record EntityIdQuery(Id EntityId, object? Options = null) : IAgentSignatu { public TFilter GetFilter(IAgentSignatureFilterBuilder builder) { - return builder.EntityIdsIn(EntityId); + return builder.SubjectIdsIn(EntityId); } public TSort GetSort(IAgentSignatureSortBuilder builder) @@ -17,10 +17,6 @@ public TSort GetSort(IAgentSignatureSortBuilder builder) return builder.EntityIds(true); } - public int? Skip => null; - - public int? Take => null; - public TFilter GetFilter(ICommandFilterBuilder builder) { return builder.EntityIdIn(EntityId); @@ -62,4 +58,8 @@ public TSort GetSort(ITagSortBuilder builder) builder.EntityVersionNumber(true) ); } + + public int? Skip => null; + + public int? Take => null; } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs index 484b7615..b6c79303 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs @@ -10,7 +10,7 @@ public record TransactionIdQuery(Id TransactionId, object? Options = null) : IAg { public TFilter GetFilter(IAgentSignatureFilterBuilder builder) { - return builder.TransactionIdIn(TransactionId); + return builder.SourceIdIn(TransactionId); } public TSort GetSort(IAgentSignatureSortBuilder builder) @@ -27,7 +27,7 @@ public TSort GetSort(IAgentSignatureSortBuilder builder) public TFilter GetFilter(ICommandFilterBuilder builder) { - return builder.TransactionIdIn(TransactionId); + return builder.SourceIdIn(TransactionId); } public TSort GetSort(ICommandSortBuilder builder) @@ -42,7 +42,7 @@ public TSort GetSort(ICommandSortBuilder builder) public TFilter GetFilter(ILeaseFilterBuilder builder) { - return builder.TransactionIdIn(TransactionId); + return builder.SourceIdIn(TransactionId); } public TSort GetSort(ILeaseSortBuilder builder) @@ -57,7 +57,7 @@ public TSort GetSort(ILeaseSortBuilder builder) public TFilter GetFilter(ITagFilterBuilder builder) { - return builder.TransactionIdIn(TransactionId); + return builder.SourceIdIn(TransactionId); } public TSort GetSort(ITagSortBuilder builder) diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs index 0f407040..7846d5a0 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs @@ -12,8 +12,8 @@ public TFilter GetFilter(IAgentSignatureFilterBuilder builder) { return builder.And ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) ); } @@ -33,8 +33,8 @@ public TFilter GetFilter(ICommandFilterBuilder builder) { return builder.And ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) ); } @@ -52,8 +52,8 @@ public TFilter GetFilter(ILeaseFilterBuilder builder) { return builder.And ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) ); } @@ -71,8 +71,8 @@ public TFilter GetFilter(ITagFilterBuilder builder) { return builder.And ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) ); } diff --git a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs index ac3ad565..6d314309 100644 --- a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs @@ -103,7 +103,7 @@ private async Task transaction.Commands.Length.ShouldBe(1); var addLeasesCommand = - transaction.Commands[0].Command.ShouldBeAssignableTo().ShouldNotBeNull(); + transaction.Commands[0].Data.ShouldBeAssignableTo().ShouldNotBeNull(); addLeasesCommand.GetLeases().ShouldNotBeEmpty(); } @@ -219,7 +219,7 @@ private async Task Generic_GivenExistingEntity_WhenAppendingNewCommand_ThenTrans transaction.Commands.Length.ShouldBe(1); - transaction.Commands[0].Command.ShouldBeEquivalentTo(new DoNothing()); + transaction.Commands[0].Data.ShouldBeEquivalentTo(new DoNothing()); } [Theory] From 793b3db90f8bf289fa853260155689e03e944359 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:38:09 -0700 Subject: [PATCH 42/93] refactor: generalized projections --- .../Projections/IProjectionRepository.cs | 17 +---- src/EntityDb.Common/Entities/IEntity.cs | 7 ++ .../Extensions/PointerExtensions.cs | 11 +++ .../Extensions/SnapshotExtensions.cs | 13 ++++ .../Projections/IProjection.cs | 25 +++---- .../Projections/ProjectionRepository.cs | 26 ++----- .../Implementations/Entities/TestEntity.cs | 16 ++-- .../Projections/OneToOneProjection.cs | 75 ++++++++++++++----- 8 files changed, 116 insertions(+), 74 deletions(-) create mode 100644 src/EntityDb.Common/Extensions/PointerExtensions.cs create mode 100644 src/EntityDb.Common/Extensions/SnapshotExtensions.cs diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index 0400c0e3..c9681b9e 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Projections; @@ -9,13 +9,8 @@ namespace EntityDb.Abstractions.Projections; /// Encapsulates the snapshot repository for a projection. /// /// The type of the projection. -public interface IProjectionRepository : IDisposableResource +public interface IProjectionRepository : ISourceRepository, IDisposableResource { - /// - /// The backing transaction repository. - /// - ITransactionRepository TransactionRepository { get; } - /// /// The backing snapshot repository. /// @@ -28,12 +23,4 @@ public interface IProjectionRepository : IDisposableResource /// A cancellation token. /// The snapshot of a for . Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default); - - /// - /// Maps a transaction and transaction step to a projection id, or default if the entity does not map to this projection. - /// - /// The transaction - /// A command in the transaction - /// The projection id for the entity, or default if none. - Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand); } diff --git a/src/EntityDb.Common/Entities/IEntity.cs b/src/EntityDb.Common/Entities/IEntity.cs index 3c4ceed6..3edb98f4 100644 --- a/src/EntityDb.Common/Entities/IEntity.cs +++ b/src/EntityDb.Common/Entities/IEntity.cs @@ -8,6 +8,13 @@ namespace EntityDb.Common.Entities; /// The type of the entity. public interface IEntity : ISnapshot { + /// + /// Returns true if is not expected to throw an exception. + /// + /// The command + /// true if is not expected to throw an exception. + static abstract bool CanReduce(object command); + /// /// Returns a new that incorporates the commands. /// diff --git a/src/EntityDb.Common/Extensions/PointerExtensions.cs b/src/EntityDb.Common/Extensions/PointerExtensions.cs new file mode 100644 index 00000000..873fff98 --- /dev/null +++ b/src/EntityDb.Common/Extensions/PointerExtensions.cs @@ -0,0 +1,11 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Extensions; + +internal static class PointerExtensions +{ + public static Pointer Previous(this Pointer pointer) + { + return pointer.Id + pointer.VersionNumber.Previous(); + } +} diff --git a/src/EntityDb.Common/Extensions/SnapshotExtensions.cs b/src/EntityDb.Common/Extensions/SnapshotExtensions.cs new file mode 100644 index 00000000..a49f96b3 --- /dev/null +++ b/src/EntityDb.Common/Extensions/SnapshotExtensions.cs @@ -0,0 +1,13 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Snapshots; + +namespace EntityDb.Common.Extensions; + +internal static class SnapshotExtensions +{ + public static Pointer GetPointer(this TSnapshot snapshot) + where TSnapshot : ISnapshot + { + return snapshot.GetId() + snapshot.GetVersionNumber(); + } +} diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs index 82cba0d5..f4caba21 100644 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ b/src/EntityDb.Common/Projections/IProjection.cs @@ -1,5 +1,5 @@ using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Snapshots; @@ -14,28 +14,23 @@ public interface IProjection : ISnapshot /// /// Returns a new that incorporates the transaction command. /// - /// - /// + /// /// - TProjection Reduce(ITransaction transaction, ITransactionCommand transactionCommand); + TProjection Reduce(ISource source); /// - /// Returns a that finds transaction commands that need to be passed to the reducer. + /// Returns a that finds transactions that need to be passed to the reducer. /// + /// The source repository, which can be used to locate new information /// A pointer to the desired projection state - /// The transaction repository, which can be used to locate new information /// A cancellation token - /// A that is used to load the rest of the transaction commands for the given projection pointer. - /// - /// I would only recommend using the transaction repository to locate leases or tags, not commands or agent signatures. - /// - Task GetCommandQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken); + /// A that is used to load the rest of the transaction commands for the given projection pointer. + IAsyncEnumerable EnumerateSources(ISourceRepository sourceRepository, Pointer projectionPointer, CancellationToken cancellationToken); /// - /// Maps an entity to a projection id, or default if the entity does not map to this projection. + /// Maps a source to a projection id, or default if the entity does not map to this projection. /// - /// The transaction that could trigger a projection - /// The transaction command that could trigger a projection + /// The source that could trigger a projection /// The projection id for the entity, or default if none. - static abstract Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand); + static abstract IEnumerable EnumerateProjectionIds(ISource source); } diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index e2c93983..167229fc 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,21 +1,15 @@ using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; -using EntityDb.Common.Extensions; -using EntityDb.Common.Queries; -using EntityDb.Common.Transactions; using Microsoft.Extensions.DependencyInjection; -using System.Collections.Immutable; -using System.Runtime.CompilerServices; namespace EntityDb.Common.Projections; -internal sealed class ProjectionRepository : DisposableResourceBaseClass, - IProjectionRepository +internal sealed class ProjectionRepository : DisposableResourceBaseClass, IProjectionRepository, ISourceRepository where TProjection : IProjection { public ProjectionRepository @@ -32,11 +26,6 @@ public ProjectionRepository public ISnapshotRepository? SnapshotRepository { get; } - public Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand) - { - return TProjection.GetProjectionIdOrDefault(transaction, transactionCommand); - } - public async Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default) { var projection = SnapshotRepository is not null @@ -44,16 +33,11 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati TProjection.Construct(projectionPointer.Id) : TProjection.Construct(projectionPointer.Id); - var newTransactionIdsQuery = await projection.GetCommandQuery(projectionPointer, TransactionRepository, cancellationToken); - - var transactions = TransactionRepository.EnumerateTransactions(newTransactionIdsQuery, cancellationToken); + var sources = projection.EnumerateSources(this, projectionPointer, cancellationToken); - await foreach (var transaction in transactions) + await foreach (var source in sources) { - foreach (var transactionCommand in transaction.Commands) - { - projection = projection.Reduce(transaction, transactionCommand); - } + projection = projection.Reduce(source); } if (!projectionPointer.IsSatisfiedBy(projection.GetVersionNumber())) diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 759f9337..ea5c2c95 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using EntityDb.Abstractions.Reducers; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Commands; @@ -40,14 +41,19 @@ public VersionNumber GetVersionNumber() return VersionNumber; } + public static bool CanReduce(object command) + { + return command is IReducer; + } + public TestEntity Reduce(object command) { - return command switch + if (command is IReducer reducer) { - DoNothing doNothing => doNothing.Reduce(this), - StoreNumber storeNumber => storeNumber.Reduce(this), - _ => throw new NotSupportedException() - }; + return reducer.Reduce(this); + } + + throw new NotSupportedException(); } public bool ShouldRecord() diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index a9935649..a3a980d6 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,13 +1,13 @@ using System.Linq.Expressions; -using EntityDb.Abstractions.Queries; +using System.Runtime.CompilerServices; using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Extensions; using EntityDb.Common.Projections; using EntityDb.Common.Queries; -using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.Common.Transactions; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; @@ -16,7 +16,7 @@ namespace EntityDb.Common.Tests.Implementations.Projections; public record OneToOneProjection : IProjection, ISnapshotWithTestLogic { public required Id Id { get; init; } - public TimeStamp LastEventAt { get; init; } + public TimeStamp LastTransactionAt { get; init; } public VersionNumber VersionNumber { get; init; } public static OneToOneProjection Construct(Id projectionId) @@ -47,18 +47,32 @@ public VersionNumber GetVersionNumber() return VersionNumber; } - public OneToOneProjection Reduce(ITransaction transaction, ITransactionCommand transactionCommand) + public OneToOneProjection Reduce(ISource source) { - if (transactionCommand.Command is not IReducer reducer) + if (source is ITransaction transaction) { - throw new NotSupportedException(); + var projection = this with + { + LastTransactionAt = transaction.TimeStamp, + }; + + foreach (var command in transaction.Commands) + { + if (command.Data is not IReducer reducer) + { + continue; + } + + projection = reducer.Reduce(projection) with + { + VersionNumber = command.EntityVersionNumber, + }; + } + + return projection; } - return reducer.Reduce(this) with - { - LastEventAt = transaction.TimeStamp, - VersionNumber = transactionCommand.EntityVersionNumber - }; + throw new NotSupportedException(); } public bool ShouldRecord() @@ -71,19 +85,44 @@ public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); } - public Task GetCommandQuery(Pointer projectionPointer, ITransactionRepository transactionRepository, CancellationToken cancellationToken) + public async IAsyncEnumerable EnumerateSources + ( + ISourceRepository sourceRepository, + Pointer projectionPointer, + [EnumeratorCancellation] CancellationToken cancellationToken + ) { - return Task.FromResult(new GetEntityCommandsQuery(projectionPointer, VersionNumber)); + var query = new GetEntityCommandsQuery(projectionPointer, VersionNumber); + + var transactionIds = await sourceRepository.TransactionRepository + .EnumerateTransactionIds(query, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var transactionId in transactionIds) + { + yield return await sourceRepository.TransactionRepository + .GetTransaction(transactionId, cancellationToken); + } } - public static Id? GetProjectionIdOrDefault(ITransaction transaction, ITransactionCommand transactionCommand) + public static IEnumerable EnumerateProjectionIds(ISource source) { - if (transactionCommand is not ITransactionCommandWithSnapshot transactionCommandWithSnapshot || transactionCommandWithSnapshot.Snapshot is not TestEntity testEntity) + var projectionIds = new HashSet(); + + if (source is ITransaction transaction) { - return null; + foreach (var command in transaction.Commands) + { + if (command.Data is not IReducer) + { + continue; + } + + projectionIds.Add(command.EntityId); + } } - return testEntity.Id; + return projectionIds; } public static string RedisKeyNamespace => "one-to-one-projection"; From 9ff226c293ad6b24df5515a080e2a5099307183e Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:38:43 -0700 Subject: [PATCH 43/93] chore: cleanup --- .../Transactions/ITransactionSubscriber.cs | 13 --- .../Entities/EntityRepository.cs | 11 +- .../Extensions/ServiceCollectionExtensions.cs | 103 ++++++++++-------- .../SourceProcessorQueueExtensions.cs | 26 +++++ .../TransactionRepositoryExtensions.cs | 73 ++++++------- .../BufferBlockTransactionReprocessorQueue.cs | 16 +-- .../ITransactionReprocessorQueueItem.cs | 2 +- .../TestModeTransactionReprocessorQueue.cs | 16 +-- .../EntitySnapshotSourceSubscriber.cs | 8 +- .../ProjectionSnapshotSourceSubscriber.cs | 8 +- .../DbContexts/EntityDbContextBase.cs | 6 - .../MongoDbTransactionRepositoryFactory.cs | 8 +- 12 files changed, 149 insertions(+), 141 deletions(-) delete mode 100644 src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs create mode 100644 src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs b/src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs deleted file mode 100644 index 542439cd..00000000 --- a/src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace EntityDb.Abstractions.Transactions; - -/// -/// Represents a type that reacts to transactions that have been committed. -/// -public interface ITransactionSubscriber -{ - /// - /// Called when a transaction has been committed (or on replay, if used in that way). - /// - /// The committed transaction. - void Notify(ITransaction transaction); -} diff --git a/src/EntityDb.Common/Entities/EntityRepository.cs b/src/EntityDb.Common/Entities/EntityRepository.cs index e3448fc9..acc13341 100644 --- a/src/EntityDb.Common/Entities/EntityRepository.cs +++ b/src/EntityDb.Common/Entities/EntityRepository.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; @@ -12,16 +13,16 @@ namespace EntityDb.Common.Entities; internal class EntityRepository : DisposableResourceBaseClass, IEntityRepository where TEntity : IEntity { - private readonly IEnumerable _transactionSubscribers; + private readonly IEnumerable _sourceSubscribers; public EntityRepository ( - IEnumerable transactionSubscribers, + IEnumerable sourceSubscribers, ITransactionRepository transactionRepository, ISnapshotRepository? snapshotRepository = null ) { - _transactionSubscribers = transactionSubscribers; + _sourceSubscribers = sourceSubscribers; TransactionRepository = transactionRepository; SnapshotRepository = snapshotRepository; } @@ -79,9 +80,9 @@ public override async ValueTask DisposeAsync() private void Publish(ITransaction transaction) { - foreach (var transactionSubscriber in _transactionSubscribers) + foreach (var sourceSubscriber in _sourceSubscribers) { - transactionSubscriber.Notify(transaction); + sourceSubscriber.Notify(transaction); } } diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 4e9cd20c..b6f6e0f4 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -1,20 +1,19 @@ using EntityDb.Abstractions.Agents; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Transactions.Builders; using EntityDb.Common.Entities; using EntityDb.Common.Projections; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.Sources.Processors.Queues; +using EntityDb.Common.Sources.ReprocessorQueues; +using EntityDb.Common.Sources.Subscribers; using EntityDb.Common.Transactions.Builders; -using EntityDb.Common.Transactions.Subscribers; -using EntityDb.Common.Transactions.Subscribers.ProcessorQueues; -using EntityDb.Common.Transactions.Subscribers.Processors; -using EntityDb.Common.Transactions.Subscribers.ReprocessorQueues; using EntityDb.Common.TypeResolvers; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Threading.Tasks.Dataflow; namespace EntityDb.Common.Extensions; @@ -43,51 +42,51 @@ internal static void Add(this IServiceCollection serviceCollection, Se serviceCollection.Add(new ServiceDescriptor(typeof(TService), typeof(TService), serviceLifetime)); } + /// + [Obsolete("Please register your TTransactionProcessor yourself. You may use any scope you want. You will need to call services.AddSourceProcessorQueue(), and you may enqueue source processing by injecting ISourceProcessorQueue and calling Enqueue. There is a generci extension method available if you don't want to implement ISourceProcessorQueueItem.", true)] + public static void AddTransactionProcessorSubscriber(this IServiceCollection serviceCollection, + bool testMode, Func transactionProcessorFactory) + { + throw new NotImplementedException(); + } + /// - /// Registers the transaction processor provided, along with a transaction processor subscriber, - /// and a transaction processor queue. For test mode, the queue is not actually a queue and will - /// immediately process the transaction. For non-test mode, the queue uses a . + /// Registers a queue for processing sources (e.g., transactions) as they are committed. + /// For test mode, the queue is not actually a queue and will immediately process the source. + /// For non-test mode, the queue uses a buffer block. /// - /// The type of the transaction processor. /// The service collection. /// Wether or not to run in test mode. - /// A factory for creating the transaction processor. [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static void AddTransactionProcessorSubscriber(this IServiceCollection serviceCollection, - bool testMode, Func transactionProcessorFactory) - where TTransactionProcessor : class, ITransactionProcessor + public static void AddSourceProcessorQueue(this IServiceCollection serviceCollection, + bool testMode) { - serviceCollection.AddSingleton(transactionProcessorFactory.Invoke); - - serviceCollection.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); - - serviceCollection.AddSingleton>(); - - serviceCollection.AddSingleton>(); - if (testMode) { - serviceCollection.AddSingleton, TestModeTransactionProcessorQueue>(); + serviceCollection.AddSingleton(); } else { - serviceCollection.AddSingleton>(); + serviceCollection.AddSingleton(); - serviceCollection.AddSingleton>(serviceProvider => serviceProvider.GetRequiredService>()); + serviceCollection.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); serviceCollection.AddHostedService(serviceProvider => - serviceProvider.GetRequiredService>()); + serviceProvider.GetRequiredService()); } } /// - /// Registers a transaction reprocessor queue. For test mode, the queue is not actually a queue and will - /// immediately reprocess transactions. For non-test mode, the queue used a . + /// Registers a queue for re-processing sources (e.g., transactions) after they have + /// already been commited (and potentially processed before). For test mode, the queue is + /// not actually a queue and will immediately reprocess sources. For non-test mode, the + /// queue uses a buffer block. /// /// The service collection. /// Wether or not to run in test mode. [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static void AddTransactionReprocessorQueue(this IServiceCollection serviceCollection, bool testMode = false) + public static void AddSourceReprocessorQueue(this IServiceCollection serviceCollection, bool testMode = false) { if (testMode) { @@ -166,23 +165,28 @@ public static void AddEntity(this IServiceCollection serviceCollection) serviceCollection.AddTransient, EntityRepositoryFactory>(); } + /// + [Obsolete("Please use AddEntitySnapshotSourceSubscriber instead. This will be removed in a future version.")] + public static void AddEntitySnapshotTransactionSubscriber(this IServiceCollection serviceCollection, + string transactionSessionOptionsName, string snapshotSessionOptionsName) + where TEntity : IEntity + { + serviceCollection.AddEntitySnapshotSourceSubscriber(transactionSessionOptionsName, snapshotSessionOptionsName); + } + /// - /// Adds a transaction subscriber that records snapshots of entities. + /// Adds a source subscriber that records snapshots of entities. /// /// The service collection. /// The agent's intent for the transaction repository. /// The agent's intent for the snapshot repository. - /// If true then snapshots will be synchronously recorded. /// The type of the entity. - /// - /// Under the hood, this registers an with equal to EntitySnapshot<> - /// - public static void AddEntitySnapshotTransactionSubscriber(this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false) + public static void AddEntitySnapshotSourceSubscriber(this IServiceCollection serviceCollection, + string transactionSessionOptionsName, string snapshotSessionOptionsName) where TEntity : IEntity { - serviceCollection.AddTransactionProcessorSubscriber(testMode, serviceProvider => EntitySnapshotTransactionProcessor.Create( - serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); + serviceCollection.AddSingleton>(); + serviceCollection.AddScoped(serviceProvider => EntitySnapshotSourceProcessor.Create(serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); } /// @@ -198,23 +202,30 @@ public static void AddProjection( .AddTransient, ProjectionRepositoryFactory>(); } + /// + [Obsolete("Please use AddProjectionSnapshotSourceSubscriber instead. This will be removed in a future version.")] + public static void AddProjectionSnapshotTransactionSubscriber( + this IServiceCollection serviceCollection, + string transactionSessionOptionsName, string snapshotSessionOptionsName) + where TProjection : IProjection + { + serviceCollection.AddProjectionSnapshotSourceSubscriber(transactionSessionOptionsName, snapshotSessionOptionsName); + } + /// /// Adds a transaction subscriber that records snapshots of projections. /// /// The service collection. /// The agent's intent for the transaction repository. /// The agent's intent for the snapshot repository. - /// If true then snapshots will be synchronously recorded. /// The type of the projection. - /// - /// Under the hood, this registers an with equal to ProjectionSnapshot<> - /// - public static void AddProjectionSnapshotTransactionSubscriber( + public static void AddProjectionSnapshotSourceSubscriber( this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false) + string transactionSessionOptionsName, string snapshotSessionOptionsName) where TProjection : IProjection { - serviceCollection.AddTransactionProcessorSubscriber(testMode, serviceProvider => ProjectionSnapshotTransactionProcessor.Create( - serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); + + serviceCollection.AddSingleton>(); + serviceCollection.AddScoped(serviceProvider => ProjectionSnapshotSourceProcessor.Create(serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); } } diff --git a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs b/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs new file mode 100644 index 00000000..3dbfa67f --- /dev/null +++ b/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs @@ -0,0 +1,26 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.Sources.Processors.Queues; + +namespace EntityDb.Common.Extensions; + +/// +/// Extensions for . +/// +public static class SourceProcessorQueueExtensions +{ + /// + /// Adds a transaction and the corresponding processor to the queue. + /// + /// The type of the + /// The ransaction processor queue + /// The transaction to process + public static void Enqueue(this ISourceProcessorQueue sourceProcessorQueue, ISource source) where TSourceProcessor : ISourceProcessor + { + sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem + { + SourceProcessorType = typeof(TSourceProcessor), + Source = source, + }); + } +} diff --git a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs index 6dea67e3..ea4a494b 100644 --- a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs +++ b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs @@ -1,55 +1,50 @@ using EntityDb.Abstractions.Queries; using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Queries; using EntityDb.Common.Transactions; -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -namespace EntityDb.Common.Extensions +namespace EntityDb.Common.Extensions; + +internal static class TransactionRepositoryExtensions { - internal static class TransactionRepositoryExtensions + public static IAsyncEnumerable EnumerateTransactionIds(this ITransactionRepository transactionRepository, IQuery query, CancellationToken cancellationToken = default) { - public static async IAsyncEnumerable EnumerateTransactions(this ITransactionRepository transactionRepository, ICommandQuery commandQuery, [EnumeratorCancellation] CancellationToken cancellationToken) + return query switch { - var allAnnotatedCommands = await transactionRepository - .EnumerateAnnotatedCommands(commandQuery, cancellationToken) - .ToLookupAsync(annotatedCommand => annotatedCommand.TransactionId, cancellationToken); - - var transactionIds = allAnnotatedCommands - .SelectMany(annotatedCommands => annotatedCommands - .Select(annotatedCommand => annotatedCommand.TransactionId)) - .Distinct() - .ToArray(); + IAgentSignatureQuery agentSignatureQuery => transactionRepository.EnumerateTransactionIds(agentSignatureQuery, cancellationToken), + ICommandQuery commandQuery => transactionRepository.EnumerateTransactionIds(commandQuery, cancellationToken), + ILeaseQuery leaseQuery => transactionRepository.EnumerateTransactionIds(leaseQuery, cancellationToken), + ITagQuery tagQuery => transactionRepository.EnumerateTransactionIds(tagQuery, cancellationToken), + _ => AsyncEnumerable.Empty(), + }; + } - var agentSignatureQuery = new GetAgentSignatures(transactionIds); + public static async Task GetTransaction(this ITransactionRepository transactionRepository, Id transactionId, CancellationToken cancellationToken) + { + var query = new GetTransactionCommandsQuery(transactionId); - var annotatedAgentSignatures = transactionRepository - .EnumerateAnnotatedAgentSignatures(agentSignatureQuery, cancellationToken); + var annotatedAgentSignature = await transactionRepository + .EnumerateAnnotatedAgentSignatures(query, cancellationToken) + .SingleAsync(cancellationToken); - await foreach (var annotatedAgentSignature in annotatedAgentSignatures) + var annotatedCommands = await transactionRepository + .EnumerateAnnotatedCommands(query, cancellationToken) + .Select(annotatedCommand => new TransactionCommand { - var annotatedCommands = allAnnotatedCommands[annotatedAgentSignature.TransactionId]; + EntityId = annotatedCommand.EntityId, + EntityVersionNumber = annotatedCommand.EntityVersionNumber, + Data = annotatedCommand.Data + }) + .ToArrayAsync(cancellationToken); - yield return new Transaction - { - Id = annotatedAgentSignature.TransactionId, - TimeStamp = annotatedAgentSignature.TransactionTimeStamp, - AgentSignature = annotatedAgentSignature.Data, - Commands = annotatedCommands - .Select(annotatedCommand => new TransactionCommand - { - EntityId = annotatedCommand.EntityId, - EntityVersionNumber = annotatedCommand.EntityVersionNumber, - Command = annotatedCommand.Data - }) - .ToImmutableArray() - }; - } - } + return new Transaction + { + Id = annotatedAgentSignature.TransactionId, + TimeStamp = annotatedAgentSignature.TransactionTimeStamp, + AgentSignature = annotatedAgentSignature.Data, + Commands = annotatedCommands.ToImmutableArray(), + }; } } diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs index 84e0ca7a..7df87047 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs @@ -14,13 +14,13 @@ internal class BufferBlockTransactionReprocessorQueue : BackgroundService, ITran private readonly BufferBlock _bufferBlock = new(); private readonly ILogger _logger; private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; - private readonly ISourceProcessorQueue _transactionProcessorQueue; + private readonly ISourceProcessorQueue _sourceProcessorQueue; - public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue transactionProcessorQueue) + public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue sourceProcessorQueue) { _logger = logger; _transactionRepositoryFactory = transactionRepositoryFactory; - _transactionProcessorQueue = transactionProcessorQueue; + _sourceProcessorQueue = sourceProcessorQueue; } public void Enqueue(ITransactionReprocessorQueueItem reprocessTransactionsRequest) @@ -42,7 +42,7 @@ protected async Task Process(ITransactionReprocessorQueueItem item, Cancellation { try { - _logger.LogDebug("Started reprocessing transactions"); + _logger.LogDebug("Started reprocessing sources"); await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(item.TransactionSessionOptionsName, cancellationToken); @@ -57,18 +57,18 @@ protected async Task Process(ITransactionReprocessorQueueItem item, Cancellation var transaction = await transactionRepository .GetTransaction(transactionId, cancellationToken); - _transactionProcessorQueue.Enqueue(new SourceProcessorQueueItem + _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem { - SourceProcessorType = item.TransactionProcessorType, + SourceProcessorType = item.SourceProcessorType, Source = transaction, }); } - _logger.LogDebug("Finished reprocessing transactions"); + _logger.LogDebug("Finished reprocessing sources"); } catch (Exception exception) { - _logger.LogError(exception, "Failed to reprocess transactions"); + _logger.LogError(exception, "Failed to reprocess sources"); } } } diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs index 5af3780b..70ec98c5 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs @@ -18,7 +18,7 @@ public interface ITransactionReprocessorQueueItem /// The type of the transaction process, which *must* /// implement . /// - Type TransactionProcessorType { get; } + Type SourceProcessorType { get; } /// /// Determines which transactions need to be reprocessed. diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs index baebc6b7..0c212284 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs @@ -9,13 +9,13 @@ internal class TestModeTransactionReprocessorQueue : ITransactionReprocessorQueu { private readonly ILogger _logger; private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; - private readonly ISourceProcessorQueue _transactionProcessorQueue; + private readonly ISourceProcessorQueue _sourceProcessorQueue; - public TestModeTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue transactionProcessorQueue) + public TestModeTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue sourceProcessorQueue) { _logger = logger; _transactionRepositoryFactory = transactionRepositoryFactory; - _transactionProcessorQueue = transactionProcessorQueue; + _sourceProcessorQueue = sourceProcessorQueue; } public void Enqueue(ITransactionReprocessorQueueItem reprocessTransactionsRequest) @@ -27,7 +27,7 @@ protected async Task Process(ITransactionReprocessorQueueItem item, Cancellation { try { - _logger.LogDebug("Started reprocessing transactions"); + _logger.LogDebug("Started reprocessing sources"); await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(item.TransactionSessionOptionsName, cancellationToken); @@ -40,18 +40,18 @@ protected async Task Process(ITransactionReprocessorQueueItem item, Cancellation var transaction = await transactionRepository .GetTransaction(transactionId, cancellationToken); - _transactionProcessorQueue.Enqueue(new SourceProcessorQueueItem + _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem { - SourceProcessorType = item.TransactionProcessorType, + SourceProcessorType = item.SourceProcessorType, Source = transaction, }); } - _logger.LogDebug("Finished reprocessing transactions"); + _logger.LogDebug("Finished reprocessing sources"); } catch (Exception exception) { - _logger.LogError(exception, "Failed to reprocess transactions"); + _logger.LogError(exception, "Failed to reprocess sources"); } } } diff --git a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs index 95f49466..13b03bd4 100644 --- a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs @@ -10,11 +10,11 @@ namespace EntityDb.Common.Sources.Subscribers; internal class EntitySnapshotSourceSubscriber : ISourceSubscriber where TEntity : IEntity { - private readonly ISourceProcessorQueue _transactionProcessorQueue; + private readonly ISourceProcessorQueue _sourceProcessorQueue; - public EntitySnapshotSourceSubscriber(ISourceProcessorQueue transactionProcessorQueue) + public EntitySnapshotSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) { - _transactionProcessorQueue = transactionProcessorQueue; + _sourceProcessorQueue = sourceProcessorQueue; } public void Notify(ISource source) @@ -24,6 +24,6 @@ public void Notify(ISource source) return; } - _transactionProcessorQueue.Enqueue>(source); + _sourceProcessorQueue.Enqueue>(source); } } diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs index 0c751675..caa7dd9b 100644 --- a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs @@ -9,11 +9,11 @@ namespace EntityDb.Common.Sources.Subscribers; internal class ProjectionSnapshotSourceSubscriber : ISourceSubscriber where TProjection : IProjection { - private readonly ISourceProcessorQueue _transactionProcessorQueue; + private readonly ISourceProcessorQueue _sourceProcessorQueue; - public ProjectionSnapshotSourceSubscriber(ISourceProcessorQueue transactionProcessorQueue) + public ProjectionSnapshotSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) { - _transactionProcessorQueue = transactionProcessorQueue; + _sourceProcessorQueue = sourceProcessorQueue; } public void Notify(ISource source) @@ -23,6 +23,6 @@ public void Notify(ISource source) return; } - _transactionProcessorQueue.Enqueue>(source); + _sourceProcessorQueue.Enqueue>(source); } } diff --git a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs index 2b6c3a7d..3fb75b6f 100644 --- a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs +++ b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs @@ -1,15 +1,9 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.EntityFramework.Converters; -using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; namespace EntityDb.EntityFramework.DbContexts; -public interface ISnapshotReferenceDbContext where TDbContext : ISnapshotReferenceDbContext -{ - static abstract Task ConstructAsync(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); -} - /// /// A DbContext that adds basic converters for types defined in /// diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs index 99579cbe..72e46486 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; namespace EntityDb.MongoDb.Transactions; @@ -17,13 +18,6 @@ internal class MongoDbTransactionRepositoryFactory : DisposableResourceBaseClass private readonly IOptionsFactory _optionsFactory; private readonly IServiceProvider _serviceProvider; - static MongoDbTransactionRepositoryFactory() - { - BsonSerializer.RegisterSerializer(new IdSerializer()); - BsonSerializer.RegisterSerializer(new TimeStampSerializer()); - BsonSerializer.RegisterSerializer(new VersionNumberSerializer()); - } - public MongoDbTransactionRepositoryFactory ( IServiceProvider serviceProvider, From 5391af88e32202ab8123295cb702f8c010e05889 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:39:24 -0700 Subject: [PATCH 44/93] fix: InMemory should not instantiate more than one repository factory in test mode --- .../Extensions/ServiceCollectionExtensions.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs index 28b675b4..1ab20c38 100644 --- a/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs @@ -28,8 +28,17 @@ public static void AddInMemorySnapshots(this IServiceCollection servi _ => new InMemorySession() ); - serviceCollection.AddScoped(serviceProvider => ActivatorUtilities - .CreateInstance>(serviceProvider) - .UseTestMode(testMode)); + serviceCollection.Add> + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient + ); + + serviceCollection.Add + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, + serviceProvider => serviceProvider + .GetRequiredService>() + .UseTestMode(testMode) + ); } } From 23a18b2e2681d5dc729f4877f09dd425f2390210 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 29 Aug 2023 23:39:34 -0700 Subject: [PATCH 45/93] fix: broken tests --- .../Seeders/TransactionCommandSeeder.cs | 17 ++++++++--------- .../Seeders/TransactionSeeder.cs | 4 ++-- .../Projections/ProjectionsTests.cs | 9 ++++++--- test/EntityDb.Common.Tests/StartupBase.cs | 4 ++++ 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs index a677bf1b..a649320c 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs @@ -8,21 +8,20 @@ namespace EntityDb.Common.Tests.Implementations.Seeders; public static class TransactionCommandSeeder { - public static IEnumerable CreateFromCommands(Id entityId, uint numCommands) + public static IEnumerable CreateFromCommands(Id entityId, uint numCommands, ulong previousVersionNumberValue = 0) where TEntity : class, IEntity, ISnapshotWithTestLogic { - for (var previousVersionNumber = new VersionNumber(0); - previousVersionNumber.Value < numCommands; - previousVersionNumber = previousVersionNumber.Next()) + var previousVersionNumber = new VersionNumber(previousVersionNumberValue); + + for (var versionNumberOffset = 0; versionNumberOffset < numCommands; versionNumberOffset++) { - var entityVersionNumber = previousVersionNumber.Next(); + previousVersionNumber = previousVersionNumber.Next(); - yield return new TransactionCommandWithSnapshot + yield return new TransactionCommand { EntityId = entityId, - Snapshot = TEntity.Construct(entityId).WithVersionNumber(entityVersionNumber), - EntityVersionNumber = entityVersionNumber, - Command = CommandSeeder.Create() + EntityVersionNumber = previousVersionNumber, + Data = CommandSeeder.Create() }; } } diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs index 46c904b1..7b81fed7 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs @@ -21,10 +21,10 @@ public static ITransaction Create(params ITransactionCommand[] transactionComman }; } - public static ITransaction Create(Id entityId, uint numCommands) + public static ITransaction Create(Id entityId, uint numCommands, ulong previousVersionNumberValue = 0) where TEntity : class, IEntity, ISnapshotWithTestLogic { - var transactionCommands = TransactionCommandSeeder.CreateFromCommands(entityId, numCommands).ToArray(); + var transactionCommands = TransactionCommandSeeder.CreateFromCommands(entityId, numCommands, previousVersionNumberValue).ToArray(); return Create(transactionCommands); } diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index a405c8b5..fbedfa7b 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -59,7 +59,8 @@ private async Task projection.GetVersionNumber() == new VersionNumber(replaceAtVersionNumber); var projectionId = Id.NewId(); - var transaction = TransactionSeeder.Create(projectionId, numberOfVersionNumbers); + var firstTransaction = TransactionSeeder.Create(projectionId, replaceAtVersionNumber); + var secondTransaction = TransactionSeeder.Create(projectionId, numberOfVersionNumbers - replaceAtVersionNumber, replaceAtVersionNumber); using var serviceScope = CreateServiceScope(serviceCollection => { @@ -76,13 +77,15 @@ private async Task .GetRequiredService>() .CreateRepository(TestSessionOptions.Write, TestSessionOptions.Write); - var transactionInserted = await entityRepository.PutTransaction(transaction); + var firstTransactionInserted = await entityRepository.PutTransaction(firstTransaction); + var secondTransactionInserted = await entityRepository.PutTransaction(secondTransaction); // ARRANGE ASSERTIONS numberOfVersionNumbers.ShouldBeGreaterThan(replaceAtVersionNumber); - transactionInserted.ShouldBeTrue(); + firstTransactionInserted.ShouldBeTrue(); + secondTransactionInserted.ShouldBeTrue(); projectionRepository.SnapshotRepository.ShouldNotBeNull(); diff --git a/test/EntityDb.Common.Tests/StartupBase.cs b/test/EntityDb.Common.Tests/StartupBase.cs index 86c466b3..2146b011 100644 --- a/test/EntityDb.Common.Tests/StartupBase.cs +++ b/test/EntityDb.Common.Tests/StartupBase.cs @@ -19,5 +19,9 @@ public virtual void AddServices(IServiceCollection serviceCollection) // Agent Accessor serviceCollection.AddAgentAccessor(); + + // Transaction Processor Queue + + serviceCollection.AddSourceProcessorQueue(true); } } \ No newline at end of file From 114d481a9433bd4f2b0b79ed61771c0a100e053e Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 17 Sep 2023 14:49:46 -0700 Subject: [PATCH 46/93] refactor: make these extensions public --- .../TransactionRepositoryExtensions.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs index ea4a494b..6c92c5cd 100644 --- a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs +++ b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs @@ -7,8 +7,18 @@ namespace EntityDb.Common.Extensions; -internal static class TransactionRepositoryExtensions +/// +/// Extensions for . +/// +public static class TransactionRepositoryExtensions { + /// + /// Enumerate transaction ids for any supported query type + /// + /// The transaction repository + /// The query + /// A cancellation token + /// Transaction ids public static IAsyncEnumerable EnumerateTransactionIds(this ITransactionRepository transactionRepository, IQuery query, CancellationToken cancellationToken = default) { return query switch @@ -21,6 +31,13 @@ public static IAsyncEnumerable EnumerateTransactionIds(this ITransactionRepo }; } + /// + /// Reconstruct the object by transaction id + /// + /// The transaction repository + /// The transaction id + /// A cancellation token + /// An instance of . public static async Task GetTransaction(this ITransactionRepository transactionRepository, Id transactionId, CancellationToken cancellationToken) { var query = new GetTransactionCommandsQuery(transactionId); From 71a027e1830d27ac7ce1e418189c39206c856cae Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 17 Sep 2023 15:31:40 -0700 Subject: [PATCH 47/93] refactor: make these source processors public so that they can be referenced by queue items --- .../Processors/EntitySnapshotSourceProcessor.cs | 10 ++++++++-- .../Processors/ProjectionSnapshotSourceProcessor.cs | 11 ++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs index bd3d1f59..281069cb 100644 --- a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs @@ -9,7 +9,11 @@ namespace EntityDb.Common.Sources.Processors; -internal class EntitySnapshotSourceProcessor : ISourceProcessor +/// +/// A source processor that creates entity snapshots +/// +/// The type of the entity +public sealed class EntitySnapshotSourceProcessor : ISourceProcessor where TEntity : IEntity { private readonly ILogger> _logger; @@ -17,6 +21,7 @@ internal class EntitySnapshotSourceProcessor : ISourceProcessor private readonly string _snapshotSessionOptionsName; private readonly string _transactionSessionOptionsName; + /// public EntitySnapshotSourceProcessor ( ILogger> logger, @@ -31,6 +36,7 @@ string snapshotSessionOptionsName _snapshotSessionOptionsName = snapshotSessionOptionsName; } + /// public async Task Process(ISource source, CancellationToken cancellationToken) { if (source is not ITransaction transaction) @@ -84,7 +90,7 @@ public async Task Process(ISource source, CancellationToken cancellationToken) } } - public static EntitySnapshotSourceProcessor Create(IServiceProvider serviceProvider, + internal static EntitySnapshotSourceProcessor Create(IServiceProvider serviceProvider, string transactionSessionOptionsName, string snapshotSessionOptionsName) { return ActivatorUtilities.CreateInstance>(serviceProvider, diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs index 8374cc55..cc994450 100644 --- a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Extensions; using EntityDb.Common.Projections; using Microsoft.Extensions.DependencyInjection; @@ -8,7 +7,11 @@ namespace EntityDb.Common.Sources.Processors; -internal sealed class ProjectionSnapshotSourceProcessor : ISourceProcessor +/// +/// A source processor that creates projection snapshots +/// +/// +public sealed class ProjectionSnapshotSourceProcessor : ISourceProcessor where TProjection : IProjection { private readonly ILogger> _logger; @@ -16,6 +19,7 @@ internal sealed class ProjectionSnapshotSourceProcessor : ISourcePr private readonly string _snapshotSessionOptionsName; private readonly string _transactionSessionOptionsName; + /// public ProjectionSnapshotSourceProcessor ( ILogger> logger, @@ -30,6 +34,7 @@ string snapshotSessionOptionsName _snapshotSessionOptionsName = snapshotSessionOptionsName; } + /// public async Task Process(ISource source, CancellationToken cancellationToken) { await using var projectionRepository = await _projectionRepositoryFactory @@ -64,7 +69,7 @@ public async Task Process(ISource source, CancellationToken cancellationToken) } } - public static ProjectionSnapshotSourceProcessor Create(IServiceProvider serviceProvider, + internal static ProjectionSnapshotSourceProcessor Create(IServiceProvider serviceProvider, string transactionSessionOptionsName, string snapshotSessionOptionsName) { return ActivatorUtilities.CreateInstance>(serviceProvider, From e5f7a1e08a7a52080900290e6950534da09115ed Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 17 Sep 2023 15:32:02 -0700 Subject: [PATCH 48/93] refactor: some unsealed classes that should be sealed --- src/EntityDb.Common/Agents/AgentAccessorChain.cs | 2 +- src/EntityDb.Common/Agents/UnknownAgentAccessor.cs | 2 +- .../Exceptions/CannotWriteInReadOnlyModeException.cs | 2 +- src/EntityDb.Common/Exceptions/NoAgentException.cs | 2 +- src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs | 2 +- .../TypeResolvers/DefaultPartialTypeResolverOptions.cs | 2 +- .../Sessions/EntityFrameworkSnapshotSessionOptions.cs | 2 +- src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs | 2 +- src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs | 2 +- src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs | 2 +- .../Sessions/MongoDbTransactionSessionOptions.cs | 2 +- src/EntityDb.Mvc/Agents/HttpContextAgentSignatureOptions.cs | 2 +- src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs | 2 +- src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs | 2 +- src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/EntityDb.Common/Agents/AgentAccessorChain.cs b/src/EntityDb.Common/Agents/AgentAccessorChain.cs index a6d9f5d2..b7f05f3b 100644 --- a/src/EntityDb.Common/Agents/AgentAccessorChain.cs +++ b/src/EntityDb.Common/Agents/AgentAccessorChain.cs @@ -12,7 +12,7 @@ namespace EntityDb.Common.Agents; /// If all instances of throw an exception, this type will throw a /// . /// -public class AgentAccessorChain : IAgentAccessor +public sealed class AgentAccessorChain : IAgentAccessor { private readonly IAgentAccessor[] _agentAccessors; private readonly ILogger _logger; diff --git a/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs b/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs index c06be995..82278f71 100644 --- a/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs +++ b/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Agents; /// /// Represents a type that indicates there is no known actor. /// -public class UnknownAgentAccessor : IAgentAccessor +public sealed class UnknownAgentAccessor : IAgentAccessor { private static readonly Dictionary DefaultApplicationInfo = new(); diff --git a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs b/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs index fddbef98..c832c704 100644 --- a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs +++ b/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs @@ -6,6 +6,6 @@ namespace EntityDb.Common.Exceptions; /// The exception that is thrown when an actor passes a to an /// that was created for read-only mode. /// -public class CannotWriteInReadOnlyModeException : Exception +public sealed class CannotWriteInReadOnlyModeException : Exception { } diff --git a/src/EntityDb.Common/Exceptions/NoAgentException.cs b/src/EntityDb.Common/Exceptions/NoAgentException.cs index 519773fb..0cfb39f8 100644 --- a/src/EntityDb.Common/Exceptions/NoAgentException.cs +++ b/src/EntityDb.Common/Exceptions/NoAgentException.cs @@ -6,6 +6,6 @@ namespace EntityDb.Common.Exceptions; /// The exception that is thrown when the cannot return an instance of /// . /// -public class NoAgentException : Exception +public sealed class NoAgentException : Exception { } diff --git a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs b/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs index 527128ad..9799d3cf 100644 --- a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs +++ b/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs @@ -12,7 +12,7 @@ namespace EntityDb.Common.Exceptions; /// /// Version Zero is reserved for an entity that has not yet been created/persisted. /// -public class VersionZeroReservedException : Exception +public sealed class VersionZeroReservedException : Exception { /// /// Throws a new if is diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs index 04e95392..8ffa0bdb 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.TypeResolvers; /// /// Options for the default partial type resolver /// -public class DefaultPartialTypeResolverOptions +public sealed class DefaultPartialTypeResolverOptions { /// /// If you version your assemblies, you may want to ignore the diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 2d28768c..71ced835 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -7,7 +7,7 @@ namespace EntityDb.EntityFramework.Sessions; /// /// Configuration options for the EntityFramework implementation of . /// -public class EntityFrameworkSnapshotSessionOptions +public sealed class EntityFrameworkSnapshotSessionOptions { /// /// This property is not used by the package. It only provides a convenient way to access diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs index d547183b..8e8cfe47 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs @@ -6,7 +6,7 @@ namespace EntityDb.EntityFramework.Snapshots; /// Represents a unique snapshot and its pointer. /// /// -public class SnapshotReference +public sealed class SnapshotReference { /// /// Te ID of the Reference record. diff --git a/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs b/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs index 394e0ee4..9677a053 100644 --- a/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs +++ b/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs @@ -3,7 +3,7 @@ /// /// Configures the in-memory snapshot repository. /// -public class InMemorySnapshotSessionOptions +public sealed class InMemorySnapshotSessionOptions { /// /// If true, indicates the agent only intends to execute queries. diff --git a/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs b/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs index d7ad85dd..c38adb46 100644 --- a/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs +++ b/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Queries; /// /// Options for configuring queries in MongoDb. /// -public class MongoDbQueryOptions +public sealed class MongoDbQueryOptions { /// /// Options for finding documents. diff --git a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs index aa2daaae..5fe286bb 100644 --- a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs +++ b/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs @@ -7,7 +7,7 @@ namespace EntityDb.MongoDb.Sessions; /// /// Configuration options for the MongoDb implementation of . /// -public class MongoDbTransactionSessionOptions +public sealed class MongoDbTransactionSessionOptions { /// /// A connection string that is compatible with diff --git a/src/EntityDb.Mvc/Agents/HttpContextAgentSignatureOptions.cs b/src/EntityDb.Mvc/Agents/HttpContextAgentSignatureOptions.cs index 174d1e69..a5d39af1 100644 --- a/src/EntityDb.Mvc/Agents/HttpContextAgentSignatureOptions.cs +++ b/src/EntityDb.Mvc/Agents/HttpContextAgentSignatureOptions.cs @@ -3,7 +3,7 @@ /// /// Configuration options for the Http Context agent. /// -public class HttpContextAgentSignatureOptions +public sealed class HttpContextAgentSignatureOptions { /// /// If there is a header whose value is sensitive and should not be recorded in the diff --git a/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs b/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs index 46f80ebe..37321049 100644 --- a/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs +++ b/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs @@ -6,7 +6,7 @@ namespace EntityDb.Npgsql.Queries; /// /// Defines query options for the Npgsql driver. /// -public class NpgsqlQueryOptions +public sealed class NpgsqlQueryOptions { /// /// Defines the collation for sorting on . diff --git a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs index 0edfe9ea..5236d92d 100644 --- a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs @@ -7,7 +7,7 @@ namespace EntityDb.Redis.Sessions; /// /// Configuration options for the Redis implementation of . /// -public class RedisSnapshotSessionOptions +public sealed class RedisSnapshotSessionOptions { /// /// A connection string that is compatible with diff --git a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs b/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs index 41ae493c..2d488f15 100644 --- a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs +++ b/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs @@ -7,7 +7,7 @@ namespace EntityDb.SqlDb.Sessions; /// /// Configuration options for the PostgreSql implementation of . /// -public class SqlDbTransactionSessionOptions +public sealed class SqlDbTransactionSessionOptions { /// /// A connection string that is compatible with From 31ffa1f4ab92f90e6e03fa65f1504feaf2caf9d7 Mon Sep 17 00:00:00 2001 From: the-avid-engineer Date: Sat, 7 Oct 2023 12:13:38 -0700 Subject: [PATCH 49/93] chore: update SDK --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index ceb31304..ade31dad 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.201", + "version": "7.0.401", "allowPrerelease": false, "rollForward": "disable" } From 19d757efea9da78636daad0e7496c1d641e6df54 Mon Sep 17 00:00:00 2001 From: the-avid-engineer Date: Sat, 7 Oct 2023 12:16:47 -0700 Subject: [PATCH 50/93] chore: rider suggestions --- .../Entities/EntityRepository.cs | 7 +--- .../Extensions/ServiceCollectionExtensions.cs | 8 ++--- .../SourceProcessorQueueExtensions.cs | 2 +- .../Projections/ProjectionRepository.cs | 2 +- .../Queries/GetAgentSignatures.cs | 2 +- .../Queues/TestModeSourceProcessorQueue.cs | 3 +- .../SingleEntityTransactionBuilder.cs | 4 +-- .../Converters/IdConverter.cs | 6 ++-- .../Converters/TimeStampConverter.cs | 6 ++-- .../Converters/VersionNumberConverter.cs | 6 ++-- .../DbContexts/IEntityDbContext.cs | 2 +- .../DbContexts/IEntityDbContextFactory.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 1 - ...bTransactionRepositoryFactoryExtensions.cs | 6 ++-- .../Predicates/PredicateBuilder.cs | 3 +- .../Sessions/EntityFrameworkSession.cs | 4 +-- .../EntityFrameworkSnapshotSessionOptions.cs | 4 +-- .../SnapshotReferenceTypeConfiguration.cs | 2 +- .../Documents/CommandDocument.cs | 4 +-- .../Extensions/ServiceCollectionExtensions.cs | 1 - src/EntityDb.MongoDb/Queries/BuilderBase.cs | 3 -- .../Queries/SortBuilders/SortBuilderBase.cs | 5 --- .../MongoDbTransactionSessionOptions.cs | 2 +- .../MongoDbTransactionRepositoryFactory.cs | 3 -- .../Converters/NpgsqlConverter.cs | 11 +++--- .../Extensions/ServiceCollectionExtensions.cs | 1 - .../Extensions/ServiceCollectionExtensions.cs | 1 - .../Sessions/RedisSnapshotSessionOptions.cs | 2 +- .../Converters/ISqlConverter.cs | 2 +- .../AgentSignatureDataDocumentReader.cs | 6 ++-- .../AgentSignatureDocumentReader.cs | 14 ++++---- .../AgentSignatureDocumentReaderBase.cs | 20 +++++------ .../AgentSignatureEntityIdsDocumentReader.cs | 6 ++-- ...entSignatureTransactionIdDocumentReader.cs | 6 ++-- .../Command/CommandDataDocumentReader.cs | 6 ++-- .../Documents/Command/CommandDocument.cs | 4 +-- .../Command/CommandDocumentReader.cs | 16 ++++----- .../Command/CommandDocumentReaderBase.cs | 24 ++++++------- .../Command/CommandEntityIdDocumentReader.cs | 6 ++-- ...ommandEntityVersionNumberDocumentReader.cs | 6 ++-- .../CommandTransactionIdDocumentReader.cs | 6 ++-- .../Lease/LeaseDataDocumentReader.cs | 6 ++-- .../Documents/Lease/LeaseDocument.cs | 23 ++++++------ .../Documents/Lease/LeaseDocumentReader.cs | 22 ++++++------ .../Lease/LeaseDocumentReaderBase.cs | 36 +++++++++---------- .../Lease/LeaseEntityIdDocumentReader.cs | 6 ++-- .../Lease/LeaseTransactionIdDocumentReader.cs | 6 ++-- .../Documents/Tag/TagDataDocumentReader.cs | 6 ++-- .../Documents/Tag/TagDocument.cs | 21 +++++------ .../Documents/Tag/TagDocumentReader.cs | 20 +++++------ .../Documents/Tag/TagDocumentReaderBase.cs | 32 ++++++++--------- .../Tag/TagEntityIdDocumentReader.cs | 6 ++-- .../Tag/TagTransactionIdDocumentReader.cs | 6 ++-- src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs | 2 +- src/EntityDb.SqlDb/Sessions/SqlDbSession.cs | 8 ++--- .../Seeder/HttpContextSeeder.cs | 2 +- 56 files changed, 196 insertions(+), 231 deletions(-) diff --git a/src/EntityDb.Common/Entities/EntityRepository.cs b/src/EntityDb.Common/Entities/EntityRepository.cs index acc13341..b3a6d8f1 100644 --- a/src/EntityDb.Common/Entities/EntityRepository.cs +++ b/src/EntityDb.Common/Entities/EntityRepository.cs @@ -41,12 +41,7 @@ public async Task GetSnapshot(Pointer entityPointer, CancellationToken var commands = TransactionRepository.EnumerateCommands(commandQuery, cancellationToken); - var entity = snapshot; - - await foreach (var command in commands) - { - entity = entity.Reduce(command); - } + var entity = await commands.AggregateAsync(snapshot, (current, command) => current.Reduce(command), cancellationToken: cancellationToken); if (!entityPointer.IsSatisfiedBy(entity.GetVersionNumber())) { diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index b6f6e0f4..8f2b23a0 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -43,7 +43,7 @@ internal static void Add(this IServiceCollection serviceCollection, Se } /// - [Obsolete("Please register your TTransactionProcessor yourself. You may use any scope you want. You will need to call services.AddSourceProcessorQueue(), and you may enqueue source processing by injecting ISourceProcessorQueue and calling Enqueue. There is a generci extension method available if you don't want to implement ISourceProcessorQueueItem.", true)] + [Obsolete("Please register your TTransactionProcessor yourself. You may use any scope you want. You will need to call services.AddSourceProcessorQueue(), and you may enqueue source processing by injecting ISourceProcessorQueue and calling Enqueue. There is a generic extension method available if you don't want to implement ISourceProcessorQueueItem.", true)] public static void AddTransactionProcessorSubscriber(this IServiceCollection serviceCollection, bool testMode, Func transactionProcessorFactory) { @@ -56,7 +56,7 @@ public static void AddTransactionProcessorSubscriber(this /// For non-test mode, the queue uses a buffer block. /// /// The service collection. - /// Wether or not to run in test mode. + /// Whether or not to run in test mode. [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] public static void AddSourceProcessorQueue(this IServiceCollection serviceCollection, bool testMode) @@ -79,12 +79,12 @@ public static void AddSourceProcessorQueue(this IServiceCollection serviceCollec /// /// Registers a queue for re-processing sources (e.g., transactions) after they have - /// already been commited (and potentially processed before). For test mode, the queue is + /// already been committed (and potentially processed before). For test mode, the queue is /// not actually a queue and will immediately reprocess sources. For non-test mode, the /// queue uses a buffer block. /// /// The service collection. - /// Wether or not to run in test mode. + /// Whether or not to run in test mode. [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] public static void AddSourceReprocessorQueue(this IServiceCollection serviceCollection, bool testMode = false) { diff --git a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs b/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs index 3dbfa67f..643af391 100644 --- a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs +++ b/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs @@ -13,7 +13,7 @@ public static class SourceProcessorQueueExtensions /// Adds a transaction and the corresponding processor to the queue. /// /// The type of the - /// The ransaction processor queue + /// The transaction processor queue /// The transaction to process public static void Enqueue(this ISourceProcessorQueue sourceProcessorQueue, ISource source) where TSourceProcessor : ISourceProcessor { diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index 167229fc..ae7249d8 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -9,7 +9,7 @@ namespace EntityDb.Common.Projections; -internal sealed class ProjectionRepository : DisposableResourceBaseClass, IProjectionRepository, ISourceRepository +internal sealed class ProjectionRepository : DisposableResourceBaseClass, IProjectionRepository where TProjection : IProjection { public ProjectionRepository diff --git a/src/EntityDb.Common/Queries/GetAgentSignatures.cs b/src/EntityDb.Common/Queries/GetAgentSignatures.cs index d16dc9ef..97383caf 100644 --- a/src/EntityDb.Common/Queries/GetAgentSignatures.cs +++ b/src/EntityDb.Common/Queries/GetAgentSignatures.cs @@ -12,7 +12,7 @@ public TFilter GetFilter(IAgentSignatureFilterBuilder builder) return builder.SourceIdIn(TransactionIds); } - public TSort? GetSort(IAgentSignatureSortBuilder builder) + public TSort GetSort(IAgentSignatureSortBuilder builder) { return builder.TransactionTimeStamp(true); } diff --git a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs index 7b414f5d..c14897fd 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs @@ -1,5 +1,4 @@ -using EntityDb.Common.Sources.Processors; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace EntityDb.Common.Sources.Processors.Queues; diff --git a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs index dada320c..c42ba394 100644 --- a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs +++ b/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Builders; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; diff --git a/src/EntityDb.EntityFramework/Converters/IdConverter.cs b/src/EntityDb.EntityFramework/Converters/IdConverter.cs index 441fcdc5..62362328 100644 --- a/src/EntityDb.EntityFramework/Converters/IdConverter.cs +++ b/src/EntityDb.EntityFramework/Converters/IdConverter.cs @@ -6,10 +6,10 @@ namespace EntityDb.EntityFramework.Converters; internal class IdConverter : ValueConverter { - private static readonly Expression> IdToGuid = (id) => id.Value; - private static readonly Expression> GuidToId = (guid) => new Id(guid); + private static readonly Expression> IdToGuid = id => id.Value; + private static readonly Expression> GuidToId = guid => new Id(guid); - public IdConverter() : base(IdToGuid, GuidToId, null) + public IdConverter() : base(IdToGuid, GuidToId) { } } diff --git a/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs b/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs index e5664e25..8235d7ac 100644 --- a/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs +++ b/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs @@ -6,10 +6,10 @@ namespace EntityDb.EntityFramework.Converters; internal class TimeStampConverter : ValueConverter { - private static readonly Expression> TimeStampToDateTime = (timeStamp) => timeStamp.Value; - private static readonly Expression> DateTimeToTimeStamp = (dateTime) => new TimeStamp(dateTime); + private static readonly Expression> TimeStampToDateTime = timeStamp => timeStamp.Value; + private static readonly Expression> DateTimeToTimeStamp = dateTime => new TimeStamp(dateTime); - public TimeStampConverter() : base(TimeStampToDateTime, DateTimeToTimeStamp, null) + public TimeStampConverter() : base(TimeStampToDateTime, DateTimeToTimeStamp) { } } diff --git a/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs b/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs index 84ef7145..5c4d2dcb 100644 --- a/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs +++ b/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs @@ -6,10 +6,10 @@ namespace EntityDb.EntityFramework.Converters; internal class VersionNumberConverter : ValueConverter { - private static readonly Expression> VersionNumberToUlong = (versionNumber) => versionNumber.Value; - private static readonly Expression> UlongToVersionNumber = (@ulong) => new VersionNumber(@ulong); + private static readonly Expression> VersionNumberToUlong = versionNumber => versionNumber.Value; + private static readonly Expression> UlongToVersionNumber = @ulong => new VersionNumber(@ulong); - public VersionNumberConverter() : base(VersionNumberToUlong, UlongToVersionNumber, null) + public VersionNumberConverter() : base(VersionNumberToUlong, UlongToVersionNumber) { } } diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs index 4661b7a2..a8cb242a 100644 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs +++ b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs @@ -7,7 +7,7 @@ namespace EntityDb.EntityFramework.DbContexts; /// A type of a that can be used for EntityDb purposes. /// /// The type of the -public interface IEntityDbContext +public interface IEntityDbContext where TDbContext : DbContext, IEntityDbContext { /// diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs index 4fba5f01..501fbbae 100644 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs +++ b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs @@ -7,7 +7,7 @@ namespace EntityDb.EntityFramework.DbContexts; /// Represents a type used to create instances of . /// /// The type of the . -public interface IEntityDbContextFactory +public interface IEntityDbContextFactory { internal TDbContext Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs index b5ff49a8..f161c7a7 100644 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs @@ -3,7 +3,6 @@ using EntityDb.EntityFramework.DbContexts; using EntityDb.EntityFramework.Snapshots; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; diff --git a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs index 629e685b..29a0326b 100644 --- a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs +++ b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs @@ -7,12 +7,12 @@ internal static class EntityFrameworkSnapshotRepositoryFactoryExtensions { [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] public static IEntityFrameworkSnapshotRepositoryFactory UseTestMode( - this IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoyFactory, + this IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory, IServiceProvider serviceProvider, bool testMode) { return testMode - ? TestModeEntityFrameworkSnapshotRepositoryFactory.Create(serviceProvider, entityFrameworkSnapshotRepositoyFactory) - : entityFrameworkSnapshotRepositoyFactory; + ? TestModeEntityFrameworkSnapshotRepositoryFactory.Create(serviceProvider, entityFrameworkSnapshotRepositoryFactory) + : entityFrameworkSnapshotRepositoryFactory; } } diff --git a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs b/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs index 244d0de0..8772b226 100644 --- a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs +++ b/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs @@ -1,5 +1,4 @@ -using EntityDb.EntityFramework.Snapshots; -using System.Linq.Expressions; +using System.Linq.Expressions; namespace EntityDb.EntityFramework.Predicates; diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs index c5d64f07..9d3ef049 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs @@ -95,11 +95,11 @@ public async Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, Cancellati { AssertNotReadOnly(); - var sapshotExists = await _snapshots + var snapshotExists = await _snapshots .Where(snapshot.GetKeyPredicate()) .AnyAsync(cancellationToken); - if (!sapshotExists) + if (!snapshotExists) { _snapshots.Add(snapshot); } diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs index 71ced835..26474aa9 100644 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs @@ -11,8 +11,8 @@ public sealed class EntityFrameworkSnapshotSessionOptions { /// /// This property is not used by the package. It only provides a convenient way to access - /// the connection string using IOptions, which does not appear to be a convienent thing - /// to do in vanilla Enitity Framework. + /// the connection string using IOptions, which does not appear to be a convenient thing + /// to do in vanilla Entity Framework. /// public string ConnectionString { get; set; } = default!; diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs index 833b58b2..333b19e9 100644 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs @@ -10,7 +10,7 @@ public class SnapshotReferenceTypeConfiguration : IEntityTypeConfigur private readonly string _snapshotReferencesTableName; /// - /// Configure the napshot Reference Type. + /// Configure the snapshot Reference Type. /// /// The name of the table for snapshot references. public SnapshotReferenceTypeConfiguration(string snapshotReferencesTableName) diff --git a/src/EntityDb.MongoDb/Documents/CommandDocument.cs b/src/EntityDb.MongoDb/Documents/CommandDocument.cs index 3652cd05..ad8e6a52 100644 --- a/src/EntityDb.MongoDb/Documents/CommandDocument.cs +++ b/src/EntityDb.MongoDb/Documents/CommandDocument.cs @@ -82,8 +82,6 @@ CancellationToken cancellationToken .Execute(mongoSession, DocumentQueryExtensions.EntityVersionNumberProjection, cancellationToken) .SingleOrDefaultAsync(cancellationToken); - return document is null - ? default - : document.EntityVersionNumber; + return document?.EntityVersionNumber ?? default; } } diff --git a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs index d6a28f28..6ceeb152 100644 --- a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -2,7 +2,6 @@ using EntityDb.Common.Extensions; using EntityDb.MongoDb.Envelopes; using EntityDb.MongoDb.Transactions; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.MongoDb.Extensions; diff --git a/src/EntityDb.MongoDb/Queries/BuilderBase.cs b/src/EntityDb.MongoDb/Queries/BuilderBase.cs index ab134302..fe05823d 100644 --- a/src/EntityDb.MongoDb/Queries/BuilderBase.cs +++ b/src/EntityDb.MongoDb/Queries/BuilderBase.cs @@ -8,7 +8,4 @@ internal abstract class BuilderBase { protected const string DataTypeNameFieldName = $"{nameof(DocumentBase.Data)}.{nameof(Envelope.Headers)}.{EnvelopeHelper.Type}"; - - protected const string DataValueFieldName = - $"{nameof(DocumentBase.Data)}.{nameof(Envelope.Value)}"; } diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs index 0a1a38f1..9d8b1793 100644 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs +++ b/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs @@ -35,9 +35,4 @@ protected static SortDefinition SortDataType(bool ascending) { return Sort(ascending, DataTypeNameFieldName); } - - protected virtual string[] GetHoistedFieldNames() - { - return Array.Empty(); - } } diff --git a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs index 5fe286bb..bf6b5f9f 100644 --- a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs +++ b/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs @@ -10,7 +10,7 @@ namespace EntityDb.MongoDb.Sessions; public sealed class MongoDbTransactionSessionOptions { /// - /// A connection string that is compatible with + /// A connection string that is compatible with /// public string ConnectionString { get; set; } = default!; diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs index 72e46486..3169d8fd 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs @@ -2,12 +2,9 @@ using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; using EntityDb.Common.Transactions; -using EntityDb.MongoDb.Serializers; using EntityDb.MongoDb.Sessions; using Microsoft.Extensions.Options; using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; namespace EntityDb.MongoDb.Transactions; diff --git a/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs b/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs index b09491b1..6104f888 100644 --- a/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs +++ b/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs @@ -9,7 +9,6 @@ using Npgsql; using NpgsqlTypes; using System.Collections; -using System.Data; using System.Data.Common; using System.Text; @@ -35,7 +34,7 @@ private static string GetEqFilter(EqFilterDefinition eqFilterDefinition, NpgsqlP return $"{eqFilterDefinition.PropertyName} = {parameterName}"; } - private static string GetInFitler(InFilterDefinition inFilterDefinition, NpgsqlParameterCollection parameters) + private static string GetInFilter(InFilterDefinition inFilterDefinition, NpgsqlParameterCollection parameters) { var parameterNames = AddParameters(parameters, inFilterDefinition.PropertyName, inFilterDefinition.PropertyValues); @@ -70,20 +69,20 @@ private static string GetFilter(IFilterDefinition filterDefinition, NpgsqlParame AndFilterDefinition andFilterDefinition => string.Join ( " AND ", - andFilterDefinition.FilterDefinitions.Select(filterDefinition => GetFilter(filterDefinition, parameters)) + andFilterDefinition.FilterDefinitions.Select(innerFilterDefinition => GetFilter(innerFilterDefinition, parameters)) ), OrFilterDefinition orFilterDefinition => string.Join ( " OR ", - orFilterDefinition.FilterDefinitions.Select(filterDefinition => GetFilter(filterDefinition, parameters)) + orFilterDefinition.FilterDefinitions.Select(innerFilterDefinition => GetFilter(innerFilterDefinition, parameters)) ), NotFilterDefinition notFilterDefinition => $"NOT {GetFilter(notFilterDefinition.FilterDefinition, parameters)}", EqFilterDefinition eqFilterDefinition => GetEqFilter(eqFilterDefinition, parameters), - InFilterDefinition inFilterDefinition => GetInFitler(inFilterDefinition, parameters), + InFilterDefinition inFilterDefinition => GetInFilter(inFilterDefinition, parameters), AnyInFilterDefinition anyInFilterDefinition => GetAnyInFilter(anyInFilterDefinition, parameters), @@ -130,7 +129,7 @@ private static string GetSort(string tableName, NpgsqlQueryOptions? options, ISo CombineSortDefinition combineSortDefinition => string.Join ( ", ", - combineSortDefinition.SortDefinitions.Select(sortDefinition => GetSort(tableName, options, sortDefinition)) + combineSortDefinition.SortDefinitions.Select(innerSortDefinition => GetSort(tableName, options, innerSortDefinition)) ), AscSortDefinition ascSortDefinition => GetAscSort(tableName, options, ascSortDefinition), diff --git a/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs index dfa0cded..45292daa 100644 --- a/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs @@ -7,7 +7,6 @@ using EntityDb.Npgsql.Transactions; using EntityDb.SqlDb.Converters; using EntityDb.SqlDb.Extensions; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.Npgsql.Extensions; diff --git a/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs index c184b7e1..096b539c 100644 --- a/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs @@ -4,7 +4,6 @@ using EntityDb.Json.Envelopes; using EntityDb.Redis.ConnectionMultiplexers; using EntityDb.Redis.Snapshots; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; diff --git a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs index 5236d92d..223cd5c0 100644 --- a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs @@ -15,7 +15,7 @@ public sealed class RedisSnapshotSessionOptions public string ConnectionString { get; set; } = default!; /// - /// Choose a key namspace for snapshots. Snapshots are stored with keys in the following format: + /// Choose a key namespace for snapshots. Snapshots are stored with keys in the following format: /// {KeyNamespace}#{SnapshotId}@{SnapshotVersionNumber} /// public string KeyNamespace { get; set; } = default!; diff --git a/src/EntityDb.SqlDb/Converters/ISqlConverter.cs b/src/EntityDb.SqlDb/Converters/ISqlConverter.cs index a089688d..a6b1cb15 100644 --- a/src/EntityDb.SqlDb/Converters/ISqlConverter.cs +++ b/src/EntityDb.SqlDb/Converters/ISqlConverter.cs @@ -5,7 +5,7 @@ namespace EntityDb.SqlDb.Converters; -internal interface ISqlConverter +internal interface ISqlConverter { string SqlType { get; } diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs index 3fba7946..ea78aaf6 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs @@ -4,12 +4,12 @@ namespace EntityDb.SqlDb.Documents.AgentSignature; internal class AgentSignatureDataDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(AgentSignatureDocument.Data), }; - public AgentSignatureDataDocumentReader() : base(_propertyNames) + public AgentSignatureDataDocumentReader() : base(PropertyNames) { } @@ -17,7 +17,7 @@ public async Task Read(DbDataReader dbDataReader, Cancel { return new AgentSignatureDocument { - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal) + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) }; } } diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs index f9dd2a39..65cbb829 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs @@ -5,7 +5,7 @@ namespace EntityDb.SqlDb.Documents.AgentSignature; internal class AgentSignatureDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(AgentSignatureDocument.TransactionId), nameof(AgentSignatureDocument.TransactionTimeStamp), @@ -14,7 +14,7 @@ internal class AgentSignatureDocumentReader : AgentSignatureDocumentReaderBase, nameof(AgentSignatureDocument.Data), }; - public AgentSignatureDocumentReader() : base(_propertyNames) + public AgentSignatureDocumentReader() : base(PropertyNames) { } @@ -22,13 +22,13 @@ public async Task Read(DbDataReader dbDataReader, Cancel { return new AgentSignatureDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(_transactionTimeStampOrdinal)), - EntityIds = (await dbDataReader.GetFieldValueAsync(_entityIdsOrdinal)) + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), + TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), + EntityIds = (await dbDataReader.GetFieldValueAsync(EntityIdsOrdinal, cancellationToken)) .Select(guid => new Id(guid)) .ToArray(), - DataType = await dbDataReader.GetFieldValueAsync(_dataTypeOrdinal), - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal), + DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), }; } } diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs index 1e8b0040..c49d3f30 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs @@ -4,11 +4,11 @@ internal abstract class AgentSignatureDocumentReaderBase { private readonly string[] _propertyNames; - protected readonly int _transactionIdOrdinal; - protected readonly int _transactionTimeStampOrdinal; - protected readonly int _entityIdsOrdinal; - protected readonly int _dataTypeOrdinal; - protected readonly int _dataOrdinal; + protected readonly int TransactionIdOrdinal; + protected readonly int TransactionTimeStampOrdinal; + protected readonly int EntityIdsOrdinal; + protected readonly int DataTypeOrdinal; + protected readonly int DataOrdinal; public string[] GetPropertyNames() => _propertyNames; @@ -16,10 +16,10 @@ protected AgentSignatureDocumentReaderBase(string[] propertyNames) { _propertyNames = propertyNames; - _transactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.TransactionId)); - _transactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.TransactionTimeStamp)); - _entityIdsOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.EntityIds)); - _dataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.DataType)); - _dataOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.Data)); + TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.TransactionId)); + TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.TransactionTimeStamp)); + EntityIdsOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.EntityIds)); + DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.DataType)); + DataOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.Data)); } } diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs index b18758d5..184e0e9e 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.AgentSignature; internal class AgentSignatureEntityIdsDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(AgentSignatureDocument.EntityIds), }; - public AgentSignatureEntityIdsDocumentReader() : base(_propertyNames) + public AgentSignatureEntityIdsDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, Cancel { return new AgentSignatureDocument { - EntityIds = (await dbDataReader.GetFieldValueAsync(_entityIdsOrdinal)) + EntityIds = (await dbDataReader.GetFieldValueAsync(EntityIdsOrdinal, cancellationToken)) .Select(guid => new Id(guid)) .ToArray() }; diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs index 09d8eb55..33d91bb7 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.AgentSignature; internal class AgentSignatureTransactionIdDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(AgentSignatureDocument.TransactionId), }; - public AgentSignatureTransactionIdDocumentReader() : base(_propertyNames) + public AgentSignatureTransactionIdDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, Cancel { return new AgentSignatureDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)) + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs index 16e4ade5..374b5584 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs @@ -4,12 +4,12 @@ namespace EntityDb.SqlDb.Documents.Command; internal class CommandDataDocumentReader : CommandDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(CommandDocument.Data), }; - public CommandDataDocumentReader() : base(_propertyNames) + public CommandDataDocumentReader() : base(PropertyNames) { } @@ -17,7 +17,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationT { return new CommandDocument { - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal) + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs index a988bd86..b3c0269a 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs @@ -88,9 +88,7 @@ CancellationToken cancellationToken .Execute(sqlDbSession, EntityVersionNumberDocumentReader, cancellationToken) .SingleOrDefaultAsync(cancellationToken); - return document is null - ? default - : document.EntityVersionNumber; + return document?.EntityVersionNumber ?? default; } public Dictionary ToDictionary() diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs index b00efe12..9491d7a0 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs @@ -5,7 +5,7 @@ namespace EntityDb.SqlDb.Documents.Command; internal class CommandDocumentReader : CommandDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(CommandDocument.TransactionId), nameof(CommandDocument.TransactionTimeStamp), @@ -15,7 +15,7 @@ internal class CommandDocumentReader : CommandDocumentReaderBase, IDocumentReade nameof(CommandDocument.Data), }; - public CommandDocumentReader() : base(_propertyNames) + public CommandDocumentReader() : base(PropertyNames) { } @@ -23,12 +23,12 @@ public async Task Read(DbDataReader dbDataReader, CancellationT { return new CommandDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(_transactionTimeStampOrdinal)), - EntityId = new Id(await dbDataReader.GetFieldValueAsync(_entityIdOrdinal)), - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(_entityVersionNumberOrdinal))), - DataType = await dbDataReader.GetFieldValueAsync(_dataTypeOrdinal), - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal), + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), + TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), + EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)), + EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))), + DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), }; } } diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs index f62c15c6..0c9eea15 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs @@ -4,12 +4,12 @@ internal abstract class CommandDocumentReaderBase { private readonly string[] _propertyNames; - protected readonly int _transactionIdOrdinal; - protected readonly int _transactionTimeStampOrdinal; - protected readonly int _entityIdOrdinal; - protected readonly int _entityVersionNumberOrdinal; - protected readonly int _dataTypeOrdinal; - protected readonly int _dataOrdinal; + protected readonly int TransactionIdOrdinal; + protected readonly int TransactionTimeStampOrdinal; + protected readonly int EntityIdOrdinal; + protected readonly int EntityVersionNumberOrdinal; + protected readonly int DataTypeOrdinal; + protected readonly int DataOrdinal; public string[] GetPropertyNames() => _propertyNames; @@ -17,11 +17,11 @@ protected CommandDocumentReaderBase(string[] propertyNames) { _propertyNames = propertyNames; - _transactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.TransactionId)); - _transactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.TransactionTimeStamp)); - _entityIdOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.EntityId)); - _entityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.EntityVersionNumber)); - _dataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.DataType)); - _dataOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.Data)); + TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.TransactionId)); + TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.TransactionTimeStamp)); + EntityIdOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.EntityId)); + EntityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.EntityVersionNumber)); + DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.DataType)); + DataOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.Data)); } } diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs index ea436dbb..356b762d 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Command; internal class CommandEntityIdDocumentReader : CommandDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(CommandDocument.EntityId), }; - public CommandEntityIdDocumentReader() : base(_propertyNames) + public CommandEntityIdDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationT { return new CommandDocument { - EntityId = new Id(await dbDataReader.GetFieldValueAsync(_entityIdOrdinal)) + EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs index 64052723..c4cc3d4a 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Command; internal class CommandEntityVersionNumberDocumentReader : CommandDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(CommandDocument.EntityVersionNumber), }; - public CommandEntityVersionNumberDocumentReader() : base(_propertyNames) + public CommandEntityVersionNumberDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationT { return new CommandDocument { - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(_entityVersionNumberOrdinal))) + EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs index c7bf96f3..b5dbbb22 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Command; internal class CommandTransactionIdDocumentReader : CommandDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(CommandDocument.TransactionId), }; - public CommandTransactionIdDocumentReader() : base(_propertyNames) + public CommandTransactionIdDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationT { return new CommandDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)) + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs index 8d246d03..f6e8d108 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs @@ -4,12 +4,12 @@ namespace EntityDb.SqlDb.Documents.Lease; internal class LeaseDataDocumentReader : LeaseDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(LeaseDocument.Data), }; - public LeaseDataDocumentReader() : base(_propertyNames) + public LeaseDataDocumentReader() : base(PropertyNames) { } @@ -17,7 +17,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationTok { return new LeaseDocument { - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal) + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs index 08b693d5..6e2ea910 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs @@ -45,20 +45,17 @@ IAddLeasesCommand addLeasesCommand ( TableName, addLeasesCommand.GetLeases() - .Select(insertLease => + .Select(insertLease => new LeaseDocument { - return new LeaseDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, - Scope = insertLease.Scope, - Label = insertLease.Label, - Value = insertLease.Value, - DataType = insertLease.GetType().Name, - Data = envelopeService.Serialize(insertLease) - }; + TransactionTimeStamp = transaction.TimeStamp, + TransactionId = transaction.Id, + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, + Scope = insertLease.Scope, + Label = insertLease.Label, + Value = insertLease.Value, + DataType = insertLease.GetType().Name, + Data = envelopeService.Serialize(insertLease) }) .ToArray() ); diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs index 08b248b1..e346949f 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs @@ -5,7 +5,7 @@ namespace EntityDb.SqlDb.Documents.Lease; internal class LeaseDocumentReader : LeaseDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(LeaseDocument.TransactionId), nameof(LeaseDocument.TransactionTimeStamp), @@ -18,7 +18,7 @@ internal class LeaseDocumentReader : LeaseDocumentReaderBase, IDocumentReader Read(DbDataReader dbDataReader, CancellationTok { return new LeaseDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(_transactionTimeStampOrdinal)), - EntityId = new Id(await dbDataReader.GetFieldValueAsync(_entityIdOrdinal)), - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(_entityVersionNumberOrdinal))), - DataType = await dbDataReader.GetFieldValueAsync(_dataTypeOrdinal), - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal), - Scope = await dbDataReader.GetFieldValueAsync(_scopeOrdinal), - Label = await dbDataReader.GetFieldValueAsync(_labelOrdinal), - Value = await dbDataReader.GetFieldValueAsync(_valueOrdinal), + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), + TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), + EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)), + EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))), + DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), + Scope = await dbDataReader.GetFieldValueAsync(ScopeOrdinal, cancellationToken), + Label = await dbDataReader.GetFieldValueAsync(LabelOrdinal, cancellationToken), + Value = await dbDataReader.GetFieldValueAsync(ValueOrdinal, cancellationToken), }; } } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs index 6184cd1e..9c300b04 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs @@ -4,15 +4,15 @@ internal abstract class LeaseDocumentReaderBase { private readonly string[] _propertyNames; - protected readonly int _transactionIdOrdinal; - protected readonly int _transactionTimeStampOrdinal; - protected readonly int _entityIdOrdinal; - protected readonly int _entityVersionNumberOrdinal; - protected readonly int _dataTypeOrdinal; - protected readonly int _dataOrdinal; - protected readonly int _scopeOrdinal; - protected readonly int _labelOrdinal; - protected readonly int _valueOrdinal; + protected readonly int TransactionIdOrdinal; + protected readonly int TransactionTimeStampOrdinal; + protected readonly int EntityIdOrdinal; + protected readonly int EntityVersionNumberOrdinal; + protected readonly int DataTypeOrdinal; + protected readonly int DataOrdinal; + protected readonly int ScopeOrdinal; + protected readonly int LabelOrdinal; + protected readonly int ValueOrdinal; public string[] GetPropertyNames() => _propertyNames; @@ -20,14 +20,14 @@ protected LeaseDocumentReaderBase(string[] propertyNames) { _propertyNames = propertyNames; - _transactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.TransactionId)); - _transactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.TransactionTimeStamp)); - _entityIdOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.EntityId)); - _entityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.EntityVersionNumber)); - _dataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.DataType)); - _dataOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Data)); - _scopeOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Scope)); - _labelOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Label)); - _valueOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Value)); + TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.TransactionId)); + TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.TransactionTimeStamp)); + EntityIdOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.EntityId)); + EntityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.EntityVersionNumber)); + DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.DataType)); + DataOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Data)); + ScopeOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Scope)); + LabelOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Label)); + ValueOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Value)); } } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs index 2a20b391..f48129a7 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Lease; internal class LeaseEntityIdDocumentReader : LeaseDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyName = + private static readonly string[] PropertyName = { nameof(LeaseDocument.EntityId), }; - public LeaseEntityIdDocumentReader() : base(_propertyName) + public LeaseEntityIdDocumentReader() : base(PropertyName) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationTok { return new LeaseDocument { - EntityId = new Id(await dbDataReader.GetFieldValueAsync(_entityIdOrdinal)) + EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs index 3aef3b6d..bc5b8c54 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Lease; internal class LeaseTransactionIdDocumentReader : LeaseDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(LeaseDocument.TransactionId), }; - public LeaseTransactionIdDocumentReader() : base(_propertyNames) + public LeaseTransactionIdDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationTok { return new LeaseDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)) + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs index 595c790b..6b4ee487 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs @@ -4,12 +4,12 @@ namespace EntityDb.SqlDb.Documents.Tag; internal class TagDataDocumentReader : TagDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(TagDocument.Data), }; - public TagDataDocumentReader() : base(_propertyNames) + public TagDataDocumentReader() : base(PropertyNames) { } @@ -17,7 +17,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationToken { return new TagDocument { - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal) + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs index bfdfa8df..45c87c09 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs @@ -44,19 +44,16 @@ IAddTagsCommand addTagsCommand ( TableName, addTagsCommand.GetTags() - .Select(insertTag => + .Select(insertTag => new TagDocument { - return new TagDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, - Label = insertTag.Label, - Value = insertTag.Value, - DataType = insertTag.GetType().Name, - Data = envelopeService.Serialize(insertTag) - }; + TransactionTimeStamp = transaction.TimeStamp, + TransactionId = transaction.Id, + EntityId = transactionCommand.EntityId, + EntityVersionNumber = transactionCommand.EntityVersionNumber, + Label = insertTag.Label, + Value = insertTag.Value, + DataType = insertTag.GetType().Name, + Data = envelopeService.Serialize(insertTag) }) .ToArray() ); diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs index c3b88336..bda2c50e 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs @@ -5,7 +5,7 @@ namespace EntityDb.SqlDb.Documents.Tag; internal class TagDocumentReader : TagDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(TagDocument.TransactionId), nameof(TagDocument.TransactionTimeStamp), @@ -17,7 +17,7 @@ internal class TagDocumentReader : TagDocumentReaderBase, IDocumentReader Read(DbDataReader dbDataReader, CancellationToken { return new TagDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(_transactionTimeStampOrdinal)), - EntityId = new Id(await dbDataReader.GetFieldValueAsync(_entityIdOrdinal)), - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(_entityVersionNumberOrdinal))), - DataType = await dbDataReader.GetFieldValueAsync(_dataTypeOrdinal), - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal), - Label = await dbDataReader.GetFieldValueAsync(_labelOrdinal), - Value = await dbDataReader.GetFieldValueAsync(_valueOrdinal), + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), + TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), + EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)), + EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))), + DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), + Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), + Label = await dbDataReader.GetFieldValueAsync(LabelOrdinal, cancellationToken), + Value = await dbDataReader.GetFieldValueAsync(ValueOrdinal, cancellationToken), }; } } diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs index af3ec606..1ab4be38 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs @@ -4,14 +4,14 @@ internal abstract class TagDocumentReaderBase { private readonly string[] _propertyNames; - protected readonly int _transactionIdOrdinal; - protected readonly int _transactionTimeStampOrdinal; - protected readonly int _entityIdOrdinal; - protected readonly int _entityVersionNumberOrdinal; - protected readonly int _dataTypeOrdinal; - protected readonly int _dataOrdinal; - protected readonly int _labelOrdinal; - protected readonly int _valueOrdinal; + protected readonly int TransactionIdOrdinal; + protected readonly int TransactionTimeStampOrdinal; + protected readonly int EntityIdOrdinal; + protected readonly int EntityVersionNumberOrdinal; + protected readonly int DataTypeOrdinal; + protected readonly int DataOrdinal; + protected readonly int LabelOrdinal; + protected readonly int ValueOrdinal; public string[] GetPropertyNames() => _propertyNames; @@ -19,13 +19,13 @@ protected TagDocumentReaderBase(string[] propertyNames) { _propertyNames = propertyNames; - _transactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.TransactionId)); - _transactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.TransactionTimeStamp)); - _entityIdOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.EntityId)); - _entityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.EntityVersionNumber)); - _dataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.DataType)); - _dataOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Data)); - _labelOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Label)); - _valueOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Value)); + TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.TransactionId)); + TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.TransactionTimeStamp)); + EntityIdOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.EntityId)); + EntityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.EntityVersionNumber)); + DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.DataType)); + DataOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Data)); + LabelOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Label)); + ValueOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Value)); } } diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs index d31e85cd..e6c8f883 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Tag; internal class TagEntityIdDocumentReader : TagDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(TagDocument.EntityId), }; - public TagEntityIdDocumentReader() : base(_propertyNames) + public TagEntityIdDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationToken { return new TagDocument { - EntityId = new Id(await dbDataReader.GetFieldValueAsync(_entityIdOrdinal)) + EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs index 5a394a9c..75ad2cf2 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs @@ -5,12 +5,12 @@ namespace EntityDb.SqlDb.Documents.Tag; internal class TagTransactionIdDocumentReader : TagDocumentReaderBase, IDocumentReader { - private static readonly string[] _propertyNames = + private static readonly string[] PropertyNames = { nameof(TagDocument.TransactionId), }; - public TagTransactionIdDocumentReader() : base(_propertyNames) + public TagTransactionIdDocumentReader() : base(PropertyNames) { } @@ -18,7 +18,7 @@ public async Task Read(DbDataReader dbDataReader, CancellationToken { return new TagDocument { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(_transactionIdOrdinal)) + TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) }; } } diff --git a/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs b/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs index 029229bb..37b79166 100644 --- a/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs +++ b/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs @@ -6,7 +6,7 @@ namespace EntityDb.SqlDb.Sessions; -internal interface ISqlDbSession : IDisposableResource +internal interface ISqlDbSession : IDisposableResource where TOptions : class { DbConnection DbConnection { get; } diff --git a/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs b/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs index c9451ff5..7dfe1e24 100644 --- a/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs +++ b/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs @@ -19,7 +19,7 @@ internal class SqlDbSession : DisposableResourceBaseClass, private readonly ISqlConverter _sqlConverter; private readonly SqlDbTransactionSessionOptions _options; - private DbTransaction? _dbTransaction = default!; + private DbTransaction? _dbTransaction; public DbConnection DbConnection { get; } @@ -83,7 +83,7 @@ CancellationToken cancellationToken public async IAsyncEnumerable Find ( IDocumentReader documentReader, - IFilterDefinition filterDefintiion, + IFilterDefinition filterDefinition, ISortDefinition? sortDefinition, int? skip, int? limit, @@ -94,7 +94,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var tableName = TDocument.TableName; - var dbQuery = _sqlConverter.ConvertQuery(tableName, documentReader, filterDefintiion, sortDefinition, skip, limit, options); + var dbQuery = _sqlConverter.ConvertQuery(tableName, documentReader, filterDefinition, sortDefinition, skip, limit, options); dbQuery.Connection = DbConnection; dbQuery.Transaction = _dbTransaction; @@ -113,7 +113,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken await dbQuery.PrepareAsync(cancellationToken); - using var reader = await dbQuery.ExecuteReaderAsync(cancellationToken); + await using var reader = await dbQuery.ExecuteReaderAsync(cancellationToken); while (await reader.ReadAsync(cancellationToken)) { diff --git a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs index 6eadad4c..496f61be 100644 --- a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs +++ b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs @@ -14,7 +14,7 @@ private static ConnectionInfo CreateConnectionInfo(HttpContextSeederOptions http connectionInfoMock .SetupGet(info => info.Id) - .Returns(Id.NewId().ToString()!); + .Returns(Id.NewId().ToString()); var faker = new Faker(); From 0d374c6e31d17abbd4a49d4feb62efc94e116891 Mon Sep 17 00:00:00 2001 From: the-avid-engineer Date: Sat, 7 Oct 2023 12:17:53 -0700 Subject: [PATCH 51/93] refactor: better generic methods (SourceTimeStamp vs TransactionTimeStamp) --- .../Queries/SortBuilders/ISortBuilder.cs | 20 +++++++++++++------ .../Queries/GetAgentSignatures.cs | 2 +- .../SortBuilders/ReverseSortBuilderBase.cs | 8 ++++---- .../Queries/SortBuilders/SortBuilderBase.cs | 4 ++-- .../Queries/SortBuilders/SortBuilderBase.cs | 4 ++-- .../Queries/TransactionIdQuery.cs | 8 ++++---- .../Queries/TransactionTimeStampQuery.cs | 8 ++++---- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs b/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs index 2fef6aa3..2e9b2ff6 100644 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs +++ b/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs @@ -6,19 +6,27 @@ /// The type of sort used by the repository. public interface ISortBuilder { + /// + [Obsolete("Please use SourceTimeStamp instead. This will be removed in a future version.")] + TSort TransactionTimeStamp(bool ascending) => SourceTimeStamp(ascending); + + /// + [Obsolete("Please use SourceId instead. This will be removed in a future version.")] + TSort TransactionId(bool ascending) => SourceId(ascending); + /// - /// Returns a that orders objects by transaction timestamp. + /// Returns a that orders objects by source timestamp. /// /// Pass true for ascending order or false for descending order. - /// A that orders objects by transaction timestamp. - TSort TransactionTimeStamp(bool ascending); + /// A that orders objects by source timestamp. + TSort SourceTimeStamp(bool ascending); /// - /// Returns a that orders objects by transaction id. + /// Returns a that orders objects by source id. /// /// Pass true for ascending order or false for descending order. - /// A that orders objects by transaction id. - TSort TransactionId(bool ascending); + /// A that orders objects by source id. + TSort SourceId(bool ascending); /// /// Returns a that orders objects ordered by a series of sorts. diff --git a/src/EntityDb.Common/Queries/GetAgentSignatures.cs b/src/EntityDb.Common/Queries/GetAgentSignatures.cs index 97383caf..2052b620 100644 --- a/src/EntityDb.Common/Queries/GetAgentSignatures.cs +++ b/src/EntityDb.Common/Queries/GetAgentSignatures.cs @@ -14,7 +14,7 @@ public TFilter GetFilter(IAgentSignatureFilterBuilder builder) public TSort GetSort(IAgentSignatureSortBuilder builder) { - return builder.TransactionTimeStamp(true); + return builder.SourceTimeStamp(true); } public int? Skip => null; diff --git a/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs b/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs index a5429608..528802cc 100644 --- a/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs +++ b/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs @@ -4,14 +4,14 @@ namespace EntityDb.Common.Queries.SortBuilders; internal abstract record ReverseSortBuilderBase(ISortBuilder SortBuilder) { - public TSort TransactionTimeStamp(bool ascending) + public TSort SourceTimeStamp(bool ascending) { - return SortBuilder.TransactionTimeStamp(!ascending); + return SortBuilder.SourceTimeStamp(!ascending); } - public TSort TransactionId(bool ascending) + public TSort SourceId(bool ascending) { - return SortBuilder.TransactionId(!ascending); + return SortBuilder.SourceId(!ascending); } public TSort Combine(params TSort[] sorts) diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs index 9d8b1793..4e67025d 100644 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs +++ b/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs @@ -9,12 +9,12 @@ internal abstract class SortBuilderBase : BuilderBase, ISortBuilder SortBuilder = Builders.Sort; - public SortDefinition TransactionTimeStamp(bool ascending) + public SortDefinition SourceTimeStamp(bool ascending) { return Sort(ascending, nameof(DocumentBase.TransactionTimeStamp)); } - public SortDefinition TransactionId(bool ascending) + public SortDefinition SourceId(bool ascending) { return Sort(ascending, nameof(DocumentBase.TransactionId)); } diff --git a/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs b/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs index 86488707..3220dbe4 100644 --- a/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs +++ b/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs @@ -6,12 +6,12 @@ namespace EntityDb.SqlDb.Queries.SortBuilders; internal abstract class SortBuilderBase : ISortBuilder { - public ISortDefinition TransactionTimeStamp(bool ascending) + public ISortDefinition SourceTimeStamp(bool ascending) { return Sort(ascending, nameof(ITransactionDocument.TransactionTimeStamp)); } - public ISortDefinition TransactionId(bool ascending) + public ISortDefinition SourceId(bool ascending) { return Sort(ascending, nameof(ITransactionDocument.TransactionId)); } diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs index b6c79303..0fc4b28d 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs @@ -17,7 +17,7 @@ public TSort GetSort(IAgentSignatureSortBuilder builder) { return builder.Combine ( - builder.TransactionId(true) + builder.SourceId(true) ); } @@ -34,7 +34,7 @@ public TSort GetSort(ICommandSortBuilder builder) { return builder.Combine ( - builder.TransactionId(true), + builder.SourceId(true), builder.EntityId(true), builder.EntityVersionNumber(true) ); @@ -49,7 +49,7 @@ public TSort GetSort(ILeaseSortBuilder builder) { return builder.Combine ( - builder.TransactionId(true), + builder.SourceId(true), builder.EntityId(true), builder.EntityVersionNumber(true) ); @@ -64,7 +64,7 @@ public TSort GetSort(ITagSortBuilder builder) { return builder.Combine ( - builder.TransactionId(true), + builder.SourceId(true), builder.EntityId(true), builder.EntityVersionNumber(true) ); diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs index 7846d5a0..2cf2db6e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs @@ -21,7 +21,7 @@ public TSort GetSort(IAgentSignatureSortBuilder builder) { return builder.Combine ( - builder.TransactionTimeStamp(true) + builder.SourceTimeStamp(true) ); } @@ -42,7 +42,7 @@ public TSort GetSort(ICommandSortBuilder builder) { return builder.Combine ( - builder.TransactionTimeStamp(true), + builder.SourceTimeStamp(true), builder.EntityId(true), builder.EntityVersionNumber(true) ); @@ -61,7 +61,7 @@ public TSort GetSort(ILeaseSortBuilder builder) { return builder.Combine ( - builder.TransactionTimeStamp(true), + builder.SourceTimeStamp(true), builder.EntityId(true), builder.EntityVersionNumber(true) ); @@ -80,7 +80,7 @@ public TSort GetSort(ITagSortBuilder builder) { return builder.Combine ( - builder.TransactionTimeStamp(true), + builder.SourceTimeStamp(true), builder.EntityId(true), builder.EntityVersionNumber(true) ); From e4c19ff7f371617e7762290ff391e133d63d892a Mon Sep 17 00:00:00 2001 From: the-avid-engineer Date: Sat, 7 Oct 2023 12:18:33 -0700 Subject: [PATCH 52/93] refactor: projections use mutation instead of reduction --- .../Reducers/IReducer.cs | 15 ++----- src/EntityDb.Abstractions/States/IMutator.cs | 14 ++++++ src/EntityDb.Abstractions/States/IReducer.cs | 16 +++++++ .../Projections/IProjection.cs | 7 ++- .../Projections/ProjectionRepository.cs | 2 +- src/EntityDb.Common/Snapshots/ISnapshot.cs | 6 +++ .../ProjectionSnapshotSourceProcessor.cs | 7 ++- .../Implementations/Commands/DoNothing.cs | 11 ++--- .../Implementations/Commands/StoreNumber.cs | 11 ++--- .../Implementations/Entities/TestEntity.cs | 8 +++- .../Projections/OneToOneProjection.cs | 44 +++++++++++-------- 11 files changed, 88 insertions(+), 53 deletions(-) create mode 100644 src/EntityDb.Abstractions/States/IMutator.cs create mode 100644 src/EntityDb.Abstractions/States/IReducer.cs diff --git a/src/EntityDb.Abstractions/Reducers/IReducer.cs b/src/EntityDb.Abstractions/Reducers/IReducer.cs index d46b1df9..9f838e1c 100644 --- a/src/EntityDb.Abstractions/Reducers/IReducer.cs +++ b/src/EntityDb.Abstractions/Reducers/IReducer.cs @@ -1,16 +1,7 @@ namespace EntityDb.Abstractions.Reducers; -/// -/// Represents a type that can reduce one state into another state. -/// -/// The state to be reduced. -public interface IReducer +/// +[Obsolete("Please use the IReducer in the States namespace. This one will be removed in a future version.")] +public interface IReducer : States.IReducer { - /// - /// Returns a new that incorporates this object into input - /// . - /// - /// The state to be reduced. - /// - TState Reduce(TState state); } diff --git a/src/EntityDb.Abstractions/States/IMutator.cs b/src/EntityDb.Abstractions/States/IMutator.cs new file mode 100644 index 00000000..60eb7ab7 --- /dev/null +++ b/src/EntityDb.Abstractions/States/IMutator.cs @@ -0,0 +1,14 @@ +namespace EntityDb.Abstractions.States; + +/// +/// Represents a type that can mutate one state into another state. +/// +/// The state to be mutated. +public interface IMutator +{ + /// + /// Incorporates this object into the input . + /// + /// The state to be mutated + void Mutate(TState state); +} diff --git a/src/EntityDb.Abstractions/States/IReducer.cs b/src/EntityDb.Abstractions/States/IReducer.cs new file mode 100644 index 00000000..818e733b --- /dev/null +++ b/src/EntityDb.Abstractions/States/IReducer.cs @@ -0,0 +1,16 @@ +namespace EntityDb.Abstractions.States; + +/// +/// Represents a type that can reduce one state into another state. +/// +/// The state to be reduced. +public interface IReducer +{ + /// + /// Returns a new that incorporates this object into input + /// . + /// + /// The state to be reduced. + /// The new state + TState Reduce(TState state); +} diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs index f4caba21..b4c5f362 100644 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ b/src/EntityDb.Common/Projections/IProjection.cs @@ -12,11 +12,10 @@ namespace EntityDb.Common.Projections; public interface IProjection : ISnapshot { /// - /// Returns a new that incorporates the transaction command. + /// Incorporates the source into the projection. /// - /// - /// - TProjection Reduce(ISource source); + /// The source of information + void Mutate(ISource source); /// /// Returns a that finds transactions that need to be passed to the reducer. diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index ae7249d8..c67ccdd4 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -37,7 +37,7 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati await foreach (var source in sources) { - projection = projection.Reduce(source); + projection.Mutate(source); } if (!projectionPointer.IsSatisfiedBy(projection.GetVersionNumber())) diff --git a/src/EntityDb.Common/Snapshots/ISnapshot.cs b/src/EntityDb.Common/Snapshots/ISnapshot.cs index 11fd26a3..321d16b4 100644 --- a/src/EntityDb.Common/Snapshots/ISnapshot.cs +++ b/src/EntityDb.Common/Snapshots/ISnapshot.cs @@ -15,6 +15,12 @@ public interface ISnapshot /// A new instance of . static abstract TSnapshot Construct(Id snapshotId); + /// + /// Creates a copy of a + /// + /// A copy of a + TSnapshot Copy(); + /// /// Returns the id of this snapshot. /// diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs index cc994450..e7ee60ed 100644 --- a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs @@ -52,7 +52,12 @@ public async Task Process(ISource source, CancellationToken cancellationToken) var previousProjection = await projectionRepository.SnapshotRepository .GetSnapshotOrDefault(projectionId, cancellationToken); - var nextProjection = (previousProjection ?? TProjection.Construct(projectionId)).Reduce(source); + var nextProjection = previousProjection == null + ? TProjection.Construct(projectionId) + : previousProjection.Copy(); + + nextProjection.Mutate(source); + var nextProjectionPointer = nextProjection.GetPointer(); if (nextProjection.ShouldRecordAsLatest(previousProjection)) diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs index 97291465..70dca995 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs @@ -1,20 +1,17 @@ using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.Tags; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; namespace EntityDb.Common.Tests.Implementations.Commands; -public record DoNothing : IReducer, IReducer +public record DoNothing : IReducer, IMutator { - public OneToOneProjection Reduce(OneToOneProjection projection) + public void Mutate(OneToOneProjection projection) { - return projection with - { - VersionNumber = projection.VersionNumber.Next() - }; + projection.VersionNumber = projection.VersionNumber.Next(); } public TestEntity Reduce(TestEntity entity) diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs index 0eda39aa..632dc94d 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Commands; using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.Tags; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Leases; @@ -9,14 +9,11 @@ namespace EntityDb.Common.Tests.Implementations.Commands; -public record StoreNumber(ulong Number) : IReducer, IReducer, IAddLeasesCommand, IAddTagsCommand +public record StoreNumber(ulong Number) : IReducer, IMutator, IAddLeasesCommand, IAddTagsCommand { - public OneToOneProjection Reduce(OneToOneProjection projection) + public void Mutate(OneToOneProjection projection) { - return projection with - { - VersionNumber = projection.VersionNumber.Next() - }; + projection.VersionNumber = projection.VersionNumber.Next(); } public TestEntity Reduce(TestEntity entity) diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index ea5c2c95..75015395 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,8 +1,7 @@ using System.Linq.Expressions; -using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; -using EntityDb.Common.Tests.Implementations.Commands; using EntityDb.Common.Tests.Implementations.Snapshots; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -21,6 +20,11 @@ public static TestEntity Construct(Id entityId) }; } + public TestEntity Copy() + { + return this with { }; + } + public static void Configure(EntityTypeBuilder testEntityBuilder) { testEntityBuilder diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 2c513b74..134be8d8 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,6 +1,6 @@ using System.Linq.Expressions; using System.Runtime.CompilerServices; -using EntityDb.Abstractions.Reducers; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; @@ -13,11 +13,11 @@ namespace EntityDb.Common.Tests.Implementations.Projections; -public record OneToOneProjection : IProjection, ISnapshotWithTestLogic +public class OneToOneProjection : IProjection, ISnapshotWithTestLogic { - public required Id Id { get; init; } - public TimeStamp LastTransactionAt { get; init; } - public VersionNumber VersionNumber { get; init; } + public required Id Id { get; set; } + public VersionNumber VersionNumber { get; set; } + public TimeStamp LastTransactionAt { get; set; } public static OneToOneProjection Construct(Id projectionId) { @@ -27,6 +27,16 @@ public static OneToOneProjection Construct(Id projectionId) }; } + public OneToOneProjection Copy() + { + return new OneToOneProjection + { + Id = Id, + VersionNumber = VersionNumber, + LastTransactionAt = LastTransactionAt, + }; + } + public static void Configure(EntityTypeBuilder oneToOneProjectionBuilder) { oneToOneProjectionBuilder @@ -47,32 +57,26 @@ public VersionNumber GetVersionNumber() return VersionNumber; } - public OneToOneProjection Reduce(ISource source) + public void Mutate(ISource source) { if (source is not ITransaction transaction) { throw new NotSupportedException(); } - var projection = this with - { - LastTransactionAt = transaction.TimeStamp, - }; + LastTransactionAt = transaction.TimeStamp; foreach (var command in transaction.Commands) { - if (command.Data is not IReducer reducer) + if (command.Data is not IMutator mutator) { continue; } - projection = reducer.Reduce(projection) with - { - VersionNumber = command.EntityVersionNumber, - }; - } + mutator.Mutate(this); - return projection; + VersionNumber = command.EntityVersionNumber; + } } public bool ShouldRecord() @@ -113,7 +117,7 @@ public static IEnumerable EnumerateProjectionIds(ISource source) { foreach (var command in transaction.Commands) { - if (command.Data is not IReducer) + if (command.Data is not IMutator) { continue; } @@ -129,7 +133,9 @@ public static IEnumerable EnumerateProjectionIds(ISource source) public OneToOneProjection WithVersionNumber(VersionNumber versionNumber) { - return this with { VersionNumber = versionNumber }; + VersionNumber = versionNumber; + + return this; } public static AsyncLocal?> ShouldRecordLogic { get; } = new(); From bdf62bfd0ecaf6faa3328a3cafb4f04d196aa6fc Mon Sep 17 00:00:00 2001 From: the-avid-engineer Date: Sat, 7 Oct 2023 12:19:10 -0700 Subject: [PATCH 53/93] chore: remove InMemory from test matrix mutations and InMemory don't mix --- test/EntityDb.Common.Tests/TestsBase.cs | 29 ------------------------- 1 file changed, 29 deletions(-) diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 0561fb90..e0117aee 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -14,8 +14,6 @@ using EntityDb.Common.Tests.Implementations.Snapshots; using EntityDb.EntityFramework.Extensions; using EntityDb.EntityFramework.Sessions; -using EntityDb.InMemory.Extensions; -using EntityDb.InMemory.Sessions; using EntityDb.MongoDb.Extensions; using EntityDb.MongoDb.Queries; using EntityDb.MongoDb.Sessions; @@ -24,14 +22,12 @@ using EntityDb.Redis.Extensions; using EntityDb.Redis.Sessions; using EntityDb.SqlDb.Sessions; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MongoDB.Driver; using Moq; using Shouldly; -using Testcontainers.MongoDb; using Xunit.Abstractions; using Xunit.DependencyInjection; using Xunit.DependencyInjection.Logging; @@ -227,36 +223,11 @@ private static SnapshotAdder RedisSnapshotAdder() }); } - private static SnapshotAdder InMemorySnapshotAdder() - where TSnapshot : class, ISnapshotWithTestLogic - { - return new SnapshotAdder($"InMemory<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => - { - serviceCollection.AddInMemorySnapshots(true); - - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); - - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => - { - options.ReadOnly = true; - }); - - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - }); - }); - } - private static IEnumerable AllSnapshotAdders() where TSnapshot : class, ISnapshotWithTestLogic { yield return EntityFrameworkSnapshotAdder(); yield return RedisSnapshotAdder(); - yield return InMemorySnapshotAdder(); } private static EntityAdder GetEntityAdder() From e6347698d02252f9e1ed444c247cf512216abf68 Mon Sep 17 00:00:00 2001 From: the-avid-engineer Date: Sat, 7 Oct 2023 12:28:18 -0700 Subject: [PATCH 54/93] refactor: Copy only needed for Projection, not in general for Snapshot --- src/EntityDb.Common/Projections/IProjection.cs | 6 ++++++ src/EntityDb.Common/Snapshots/ISnapshot.cs | 6 ------ .../Implementations/Entities/TestEntity.cs | 5 ----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs index b4c5f362..20c9a84d 100644 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ b/src/EntityDb.Common/Projections/IProjection.cs @@ -11,6 +11,12 @@ namespace EntityDb.Common.Projections; /// public interface IProjection : ISnapshot { + /// + /// Creates a copy of a + /// + /// A copy of a + TProjection Copy(); + /// /// Incorporates the source into the projection. /// diff --git a/src/EntityDb.Common/Snapshots/ISnapshot.cs b/src/EntityDb.Common/Snapshots/ISnapshot.cs index 321d16b4..11fd26a3 100644 --- a/src/EntityDb.Common/Snapshots/ISnapshot.cs +++ b/src/EntityDb.Common/Snapshots/ISnapshot.cs @@ -15,12 +15,6 @@ public interface ISnapshot /// A new instance of . static abstract TSnapshot Construct(Id snapshotId); - /// - /// Creates a copy of a - /// - /// A copy of a - TSnapshot Copy(); - /// /// Returns the id of this snapshot. /// diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 75015395..f8c63e81 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -20,11 +20,6 @@ public static TestEntity Construct(Id entityId) }; } - public TestEntity Copy() - { - return this with { }; - } - public static void Configure(EntityTypeBuilder testEntityBuilder) { testEntityBuilder From 386c55721bd25b2e480c2411e1bbd1c3861a88de Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 7 Oct 2023 15:05:18 -0700 Subject: [PATCH 55/93] chore: update Copyright to 2023 --- LICENSE.txt | 2 +- src/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index f190c3c5..ff8a1263 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 - 2022 entitydb.io +Copyright (c) 2021 - 2023 entitydb.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 1fda23bd..86d7917d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -13,7 +13,7 @@ snupkg Chris Philips entitydb.io - 2021 - 2022 © entitydb.io + 2021 - 2023 © entitydb.io https://github.com/entitydb-io/entitydb git en From 6606a5eeb15af37653c190987ee2b3eda1471365 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 7 Oct 2023 15:06:37 -0700 Subject: [PATCH 56/93] refactor: remove Copy, this is the whole problem --- src/EntityDb.Common/Projections/IProjection.cs | 8 +------- .../Processors/ProjectionSnapshotSourceProcessor.cs | 2 +- .../Implementations/Projections/OneToOneProjection.cs | 10 ---------- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs index 20c9a84d..221ba73d 100644 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ b/src/EntityDb.Common/Projections/IProjection.cs @@ -10,13 +10,7 @@ namespace EntityDb.Common.Projections; /// /// public interface IProjection : ISnapshot -{ - /// - /// Creates a copy of a - /// - /// A copy of a - TProjection Copy(); - +{ /// /// Incorporates the source into the projection. /// diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs index e7ee60ed..d0b19a83 100644 --- a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs @@ -54,7 +54,7 @@ public async Task Process(ISource source, CancellationToken cancellationToken) var nextProjection = previousProjection == null ? TProjection.Construct(projectionId) - : previousProjection.Copy(); + : previousProjection; nextProjection.Mutate(source); diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 134be8d8..c5bdfd4d 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -27,16 +27,6 @@ public static OneToOneProjection Construct(Id projectionId) }; } - public OneToOneProjection Copy() - { - return new OneToOneProjection - { - Id = Id, - VersionNumber = VersionNumber, - LastTransactionAt = LastTransactionAt, - }; - } - public static void Configure(EntityTypeBuilder oneToOneProjectionBuilder) { oneToOneProjectionBuilder From 40600159a1dd89491de69cbc2fbae74de8f76028 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 7 Oct 2023 16:03:41 -0700 Subject: [PATCH 57/93] refactor: need IServiceProvider to get logging back --- .../DbContexts/EntityDbContextFactory.cs | 8 +++++--- .../DbContexts/IEntityDbContext.cs | 3 ++- .../Implementations/DbContexts/GenericDbContext.cs | 11 ++++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs index ca8d5471..3fecf263 100644 --- a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs +++ b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs @@ -7,20 +7,22 @@ namespace EntityDb.EntityFramework.DbContexts; internal class EntityDbContextFactory : IEntityDbContextFactory where TDbContext : DbContext, IEntityDbContext { + private readonly IServiceProvider _serviceProvider; private readonly IOptionsFactory _optionsFactory; - public EntityDbContextFactory(IOptionsFactory optionsFactory) + public EntityDbContextFactory(IServiceProvider serviceProvider, IOptionsFactory optionsFactory) { + _serviceProvider = serviceProvider; _optionsFactory = optionsFactory; } public TDbContext Create(string snapshotSessionOptionsName) { - return TDbContext.Construct(_optionsFactory.Create(snapshotSessionOptionsName)); + return TDbContext.Construct(_serviceProvider, _optionsFactory.Create(snapshotSessionOptionsName)); } TDbContext IEntityDbContextFactory.Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) { - return TDbContext.Construct(snapshotSessionOptions); + return TDbContext.Construct(_serviceProvider, snapshotSessionOptions); } } diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs index a8cb242a..af6660b0 100644 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs +++ b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs @@ -13,7 +13,8 @@ public interface IEntityDbContext /// /// Returns a new that will be configured using . /// + /// A service provider for any injectable dependencies /// The options for the database /// A new that will be configured using . - static abstract TDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); + static abstract TDbContext Construct(IServiceProvider serviceProvider, EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); } diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs index 97ac65dc..9db73896 100644 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs @@ -3,16 +3,20 @@ using EntityDb.EntityFramework.Sessions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace EntityDb.Common.Tests.Implementations.DbContexts; internal class GenericDbContext : EntityDbContextBase, IEntityDbContext> where TSnapshot : class, ISnapshotWithTestLogic { + private readonly ILoggerFactory _loggerFactory; private readonly EntityFrameworkSnapshotSessionOptions _options; - public GenericDbContext(EntityFrameworkSnapshotSessionOptions options) + public GenericDbContext(ILoggerFactory loggerFactory, EntityFrameworkSnapshotSessionOptions options) { + _loggerFactory = loggerFactory; _options = options; } @@ -20,12 +24,13 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseNpgsql($"{_options.ConnectionString};Include Error Detail=true") + .UseLoggerFactory(_loggerFactory) .EnableSensitiveDataLogging(); } - public static GenericDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) + public static GenericDbContext Construct(IServiceProvider serviceProvider, EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) { - return new GenericDbContext(entityFrameworkSnapshotSessionOptions); + return ActivatorUtilities.CreateInstance>(serviceProvider, entityFrameworkSnapshotSessionOptions); } protected override void OnModelCreating(ModelBuilder modelBuilder) From 152d4e9725789eba1286983e552022eb8279227c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 22 Oct 2023 11:45:35 -0700 Subject: [PATCH 58/93] feat: MongoDb snapshots --- ...itory.cs => TestModeSnapshotRepository.cs} | 0 .../Commands/DeleteDocumentsCommand.cs | 2 +- .../Commands/InsertDocumentsCommand.cs | 2 +- .../Documents/CommandDocument.cs | 2 +- .../Documents/SnapshotDocument.cs | 14 + src/EntityDb.MongoDb/EntityDb.MongoDb.csproj | 3 + .../Extensions/DocumentQueryExtensions.cs | 2 +- .../Extensions/MongoClientExtensions.cs | 69 ++++- ...goDbSnapshotRepositoryFactoryExtensions.cs | 27 ++ ...bTransactionRepositoryFactoryExtensions.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 34 ++- src/EntityDb.MongoDb/Queries/DocumentQuery.cs | 2 +- ...sionMongoDbTransactionRepositoryFactory.cs | 82 ++++++ .../IMongoDbSnapshotRepositoryFactory.cs | 24 ++ .../Snapshots/MongoDbSnapshotRepository.cs | 81 ++++++ .../MongoDbSnapshotRepositoryFactory.cs | 71 +++++ ...MongoDbSnapshotRepositoryFactoryWrapper.cs | 41 +++ .../Snapshots/Sessions/IMongoSession.cs | 24 ++ .../Sessions/MongoDbSnapshotSessionOptions.cs | 49 ++++ .../Snapshots/Sessions/MongoSession.cs | 261 ++++++++++++++++++ .../Sessions/TestModeMongoSession.cs | 54 ++++ ...estModeMongoDbSnapshotRepositoryFactory.cs | 52 ++++ ...sionMongoDbTransactionRepositoryFactory.cs | 4 +- .../IMongoDbTransactionRepositoryFactory.cs | 2 +- .../MongoDbTransactionRepository.cs | 2 +- .../MongoDbTransactionRepositoryFactory.cs | 4 +- ...goDbTransactionRepositoryFactoryWrapper.cs | 2 +- .../Sessions/IMongoSession.cs | 2 +- .../MongoDbTransactionSessionOptions.cs | 2 +- .../Sessions/MongoSession.cs | 2 +- .../Sessions/TestModeMongoSession.cs | 2 +- ...ModeMongoDbTransactionRepositoryFactory.cs | 2 +- .../Atlas/Cluster/CreateCollections.cs | 2 +- .../Serverless/CreateCollectionsServerless.cs | 2 +- .../Commands/MongoDb/CreateCollections.cs | 2 +- .../Implementations/Entities/TestEntity.cs | 1 + .../Projections/OneToOneProjection.cs | 1 + .../Snapshots/ISnapshotWithTestLogic.cs | 1 + .../Snapshots/SnapshotTests.cs | 2 +- test/EntityDb.Common.Tests/TestsBase.cs | 59 +++- .../Sessions/MongoSessionTests.cs | 2 +- 41 files changed, 955 insertions(+), 39 deletions(-) rename src/EntityDb.Common/Snapshots/{TestModeRedisSnapshotRepository.cs => TestModeSnapshotRepository.cs} (100%) create mode 100644 src/EntityDb.MongoDb/Documents/SnapshotDocument.cs create mode 100644 src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs create mode 100644 src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs rename src/EntityDb.MongoDb/{ => Transactions}/Sessions/IMongoSession.cs (92%) rename src/EntityDb.MongoDb/{ => Transactions}/Sessions/MongoDbTransactionSessionOptions.cs (96%) rename src/EntityDb.MongoDb/{ => Transactions}/Sessions/MongoSession.cs (96%) rename src/EntityDb.MongoDb/{ => Transactions}/Sessions/TestModeMongoSession.cs (94%) diff --git a/src/EntityDb.Common/Snapshots/TestModeRedisSnapshotRepository.cs b/src/EntityDb.Common/Snapshots/TestModeSnapshotRepository.cs similarity index 100% rename from src/EntityDb.Common/Snapshots/TestModeRedisSnapshotRepository.cs rename to src/EntityDb.Common/Snapshots/TestModeSnapshotRepository.cs diff --git a/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs index 0e9930fc..a3e873e2 100644 --- a/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs @@ -1,4 +1,4 @@ -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using MongoDB.Bson; using MongoDB.Driver; diff --git a/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs b/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs index 7494c333..e8c305dd 100644 --- a/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs @@ -1,4 +1,4 @@ -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; namespace EntityDb.MongoDb.Commands; diff --git a/src/EntityDb.MongoDb/Documents/CommandDocument.cs b/src/EntityDb.MongoDb/Documents/CommandDocument.cs index ad8e6a52..cf0980ff 100644 --- a/src/EntityDb.MongoDb/Documents/CommandDocument.cs +++ b/src/EntityDb.MongoDb/Documents/CommandDocument.cs @@ -8,7 +8,7 @@ using EntityDb.MongoDb.Queries; using EntityDb.MongoDb.Queries.FilterBuilders; using EntityDb.MongoDb.Queries.SortBuilders; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using MongoDB.Bson; namespace EntityDb.MongoDb.Documents; diff --git a/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs b/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs new file mode 100644 index 00000000..53f7ef50 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs @@ -0,0 +1,14 @@ +using EntityDb.Abstractions.ValueObjects; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace EntityDb.MongoDb.Documents; + +internal sealed record SnapshotDocument +{ + [BsonIgnoreIfNull] public ObjectId? _id { get; init; } + public string DataType { get; init; } = default!; + public BsonDocument Data { get; init; } = default!; + public Id PointerId { get; init; } + public VersionNumber PointerVersionNumber { get; init; } +} diff --git a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj index f979ad1d..143f5b50 100644 --- a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj +++ b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj @@ -14,4 +14,7 @@ + + + \ No newline at end of file diff --git a/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs b/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs index 6e6d8c47..e4690aeb 100644 --- a/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs @@ -5,7 +5,7 @@ using EntityDb.Common.Polyfills; using EntityDb.MongoDb.Documents; using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using MongoDB.Bson; using MongoDB.Driver; using System.Runtime.CompilerServices; diff --git a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs index d4c54cb6..3e4bc810 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs @@ -15,10 +15,15 @@ private static readonly IndexKeysDefinitionBuilder private static readonly CreateIndexOptions UniquenessConstraint = new() { Name = "Uniqueness Constraint", - Unique = true + Unique = true, }; - private static readonly Dictionary[]> Collections = new() + private static readonly CreateIndexOptions LookupIndex = new() + { + Name = "Lookup Index", + }; + + private static readonly Dictionary[]> TransactionCollections = new() { [AgentSignatureDocument.CollectionName] = new[] { @@ -65,11 +70,31 @@ private static readonly IndexKeysDefinitionBuilder IndexKeysBuilder.Descending(nameof(TagDocument.Label)), IndexKeysBuilder.Descending(nameof(TagDocument.Value)) ), - new CreateIndexOptions { Name = "Lookup Index" } + LookupIndex ) } }; + private static readonly CreateIndexModel[] SnapshotCollection = new[] + { + new CreateIndexModel + ( + IndexKeysBuilder.Combine + ( + IndexKeysBuilder.Descending(nameof(SnapshotDocument.PointerId)), + IndexKeysBuilder.Descending(nameof(SnapshotDocument.PointerVersionNumber)) + ), + UniquenessConstraint + ), + }; + + [Obsolete("Please use ProvisionTransactionCollections instead. This will be removed in a later version.")] + public static Task ProvisionCollections(this IMongoClient mongoClient, string serviceName, + CancellationToken cancellationToken = default) + { + return ProvisionTransactionCollections(mongoClient, serviceName, cancellationToken); + } + /// /// Provisions the needed collections on the database. /// @@ -81,12 +106,12 @@ private static readonly IndexKeysDefinitionBuilder /// You should ONLY use this in your code for integration testing. Real databases should be provisioned using the /// dotnet tool EntityDb.MongoDb.Provisioner. /// - public static async Task ProvisionCollections(this IMongoClient mongoClient, string serviceName, + public static async Task ProvisionTransactionCollections(this IMongoClient mongoClient, string serviceName, CancellationToken cancellationToken = default) { var mongoDatabase = mongoClient.GetDatabase(serviceName); - foreach (var (collectionName, collectionIndices) in Collections) + foreach (var (collectionName, collectionIndices) in TransactionCollections) { var mongoCollection = mongoDatabase.GetCollection(collectionName); @@ -104,4 +129,38 @@ public static async Task ProvisionCollections(this IMongoClient mongoClient, str await mongoCollection.Indexes.CreateManyAsync(collectionIndices, cancellationToken); } } + + /// + /// Provisions the needed collections on the database. + /// + /// The mongo client. + /// The name of the service, which is used as the name of the database. + /// A cancellation token. + /// An asynchronous task that, when complete, signals that the collections have been provisioned. + /// + /// You should ONLY use this in your code for integration testing. Real databases should be provisioned using the + /// dotnet tool EntityDb.MongoDb.Provisioner. + /// + public static async Task ProvisionSnapshotCollection(this IMongoClient mongoClient, string serviceName, string collectionName, + CancellationToken cancellationToken = default) + { + var collectionIndices = SnapshotCollection; + + var mongoDatabase = mongoClient.GetDatabase(serviceName); + + var mongoCollection = mongoDatabase.GetCollection(collectionName); + + var entityCollectionNameCursor = + await mongoDatabase.ListCollectionNamesAsync(cancellationToken: cancellationToken); + var entityCollectionNames = await entityCollectionNameCursor.ToListAsync(cancellationToken); + + if (entityCollectionNames.Contains(collectionName)) + { + return; + } + + await mongoDatabase.CreateCollectionAsync(collectionName, cancellationToken: cancellationToken); + + await mongoCollection.Indexes.CreateManyAsync(collectionIndices, cancellationToken); + } } diff --git a/src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs new file mode 100644 index 00000000..46145d9c --- /dev/null +++ b/src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs @@ -0,0 +1,27 @@ +using EntityDb.MongoDb.Snapshots; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.MongoDb.Extensions; + +internal static class MongoDbSnapshotRepositoryFactoryExtensions +{ + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static IMongoDbSnapshotRepositoryFactory UseTestMode( + this IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory, + bool testMode) + { + return testMode + ? new TestModeMongoDbSnapshotRepositoryFactory(mongoDbSnapshotRepositoryFactory) + : mongoDbSnapshotRepositoryFactory; + } + + public static IMongoDbSnapshotRepositoryFactory UseAutoProvision( + this IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory, + IServiceProvider serviceProvider, + bool autoProvision) + { + return autoProvision + ? AutoProvisionMongoDbSnapshotRepositoryFactory.Create(serviceProvider, mongoDbSnapshotRepositoryFactory) + : mongoDbSnapshotRepositoryFactory; + } +} diff --git a/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs index fdd2506a..67ef0f4c 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs @@ -15,7 +15,7 @@ public static IMongoDbTransactionRepositoryFactory UseTestMode( : mongoDbTransactionRepositoryFactory; } - public static IMongoDbTransactionRepositoryFactory UseAuthProvision( + public static IMongoDbTransactionRepositoryFactory UseAutoProvision( this IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory, IServiceProvider serviceProvider, bool autoProvision) diff --git a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs index 6ceeb152..097d64a9 100644 --- a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -1,6 +1,8 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Transactions; using EntityDb.Common.Extensions; using EntityDb.MongoDb.Envelopes; +using EntityDb.MongoDb.Snapshots; using EntityDb.MongoDb.Transactions; using Microsoft.Extensions.DependencyInjection; @@ -41,7 +43,35 @@ public static void AddMongoDbTransactions(this IServiceCollection serviceCollect serviceProvider => serviceProvider .GetRequiredService() .UseTestMode(testMode) - .UseAuthProvision(serviceProvider, autoProvision) + .UseAutoProvision(serviceProvider, autoProvision) + ); + } + + /// + /// Adds a production-ready implementation of to a service + /// collection. + /// + /// The service collection. + /// Modifies the behavior of the repository to accomodate tests. + /// Modifies the behavior of the repository to auto-provision collections. + public static void AddMongoDbSnapshots(this IServiceCollection serviceCollection, + bool testMode = false, bool autoProvision = false) + where TSnapshot : notnull + { + serviceCollection.AddBsonDocumentEnvelopeService(true); + + serviceCollection.Add> + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient + ); + + serviceCollection.Add> + ( + testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, + serviceProvider => serviceProvider + .GetRequiredService>() + .UseTestMode(testMode) + .UseAutoProvision(serviceProvider, autoProvision) ); } } diff --git a/src/EntityDb.MongoDb/Queries/DocumentQuery.cs b/src/EntityDb.MongoDb/Queries/DocumentQuery.cs index c1f76a19..f8e4cdec 100644 --- a/src/EntityDb.MongoDb/Queries/DocumentQuery.cs +++ b/src/EntityDb.MongoDb/Queries/DocumentQuery.cs @@ -1,4 +1,4 @@ -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using MongoDB.Bson; using MongoDB.Driver; diff --git a/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs new file mode 100644 index 00000000..7ff4664a --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs @@ -0,0 +1,82 @@ +using EntityDb.MongoDb.Extensions; +using EntityDb.MongoDb.Snapshots.Sessions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.MongoDb.Snapshots; + +internal sealed class AutoProvisionMongoDbSnapshotRepositoryFactory : MongoDbSnapshotRepositoryFactoryWrapper +{ + // ReSharper disable once StaticMemberInGenericType + private static readonly SemaphoreSlim Lock = new(1); + + // ReSharper disable once StaticMemberInGenericType + private static bool _provisioned; + + private readonly ILogger> _logger; + + public AutoProvisionMongoDbSnapshotRepositoryFactory + ( + ILogger> logger, + IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory + ) + : base(mongoDbSnapshotRepositoryFactory) + { + _logger = logger; + } + + private async Task AcquireLock(CancellationToken cancellationToken) + { + _logger.LogInformation("Wait for MongoDb Auto-Provisioning Lock"); + + await Lock.WaitAsync(cancellationToken); + + _logger.LogInformation("MongoDb Auto-Provisioning Lock Acquired"); + } + + private void ReleaseLock() + { + _logger.LogInformation("Release MongoDb Auto-Provisioning Lock"); + + Lock.Release(); + + _logger.LogInformation("MongoDb Auto-Provisioning Lock Released"); + } + + public override async Task CreateSession(MongoDbSnapshotSessionOptions options, + CancellationToken cancellationToken) + { + var mongoSession = await base.CreateSession(options, cancellationToken); + + await AcquireLock(cancellationToken); + + if (_provisioned) + { + ReleaseLock(); + + return mongoSession; + } + + await mongoSession.MongoDatabase.Client.ProvisionSnapshotCollection + ( + mongoSession.MongoDatabase.DatabaseNamespace.DatabaseName, + mongoSession.CollectionName, + cancellationToken + ); + + _provisioned = true; + + _logger.LogInformation("MongoDb has been auto-provisioned"); + + ReleaseLock(); + + return mongoSession; + } + + public static IMongoDbSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, + IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, + mongoDbSnapshotRepositoryFactory); + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs new file mode 100644 index 00000000..ab3e90e6 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.MongoDb.Snapshots.Sessions; + +namespace EntityDb.MongoDb.Snapshots; + +internal interface IMongoDbSnapshotRepositoryFactory : ISnapshotRepositoryFactory +{ + async Task> ISnapshotRepositoryFactory.CreateRepository( + string snapshotSessionOptionsName, CancellationToken cancellationToken) + { + var options = GetSessionOptions(snapshotSessionOptionsName); + + var mongoSession = await CreateSession(options, cancellationToken); + + return CreateRepository(mongoSession); + } + + MongoDbSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName); + + Task CreateSession(MongoDbSnapshotSessionOptions options, + CancellationToken cancellationToken); + + ISnapshotRepository CreateRepository(IMongoSession mongoSession); +} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs new file mode 100644 index 00000000..8d8d9fb9 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs @@ -0,0 +1,81 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.MongoDb.Documents; +using EntityDb.MongoDb.Snapshots.Sessions; +using MongoDB.Bson; + +namespace EntityDb.MongoDb.Snapshots; + +internal class MongoDbSnapshotRepository : DisposableResourceBaseClass, ISnapshotRepository + where TSnapshot : notnull +{ + private readonly IEnvelopeService _envelopeService; + private readonly IMongoSession _mongoSession; + + public MongoDbSnapshotRepository + ( + IEnvelopeService envelopeService, + IMongoSession mongoSession + ) + { + _envelopeService = envelopeService; + _mongoSession = mongoSession; + } + + public async Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, + CancellationToken cancellationToken = default) + { + try + { + _mongoSession.StartTransaction(); + + await _mongoSession.Upsert(new SnapshotDocument + { + DataType = snapshot.GetType().Name, + Data = _envelopeService.Serialize(snapshot), + PointerId = snapshotPointer.Id, + PointerVersionNumber = snapshotPointer.VersionNumber, + }, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + await _mongoSession.CommitTransaction(cancellationToken); + + return true; + } + catch + { + await _mongoSession.AbortTransaction(); + + throw; + } + } + + public async Task GetSnapshotOrDefault(Pointer snapshotPointer, + CancellationToken cancellationToken = default) + { + var snapshotDocument = await _mongoSession.Find(snapshotPointer, cancellationToken); + + if (snapshotDocument == null) + { + return default; + } + + return _envelopeService + .Deserialize(snapshotDocument.Data); + } + + public async Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) + { + await _mongoSession.Delete(snapshotPointers, cancellationToken); + + return true; + } + + public override async ValueTask DisposeAsync() + { + await _mongoSession.DisposeAsync(); + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs new file mode 100644 index 00000000..c58f50ce --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs @@ -0,0 +1,71 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Snapshots; +using EntityDb.MongoDb.Snapshots.Sessions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Snapshots; + +internal class MongoDbSnapshotRepositoryFactory : DisposableResourceBaseClass, + IMongoDbSnapshotRepositoryFactory + where TSnapshot : notnull +{ + private readonly IEnvelopeService _envelopeService; + private readonly IOptionsFactory _optionsFactory; + private readonly IServiceProvider _serviceProvider; + + public MongoDbSnapshotRepositoryFactory + ( + IServiceProvider serviceProvider, + IOptionsFactory optionsFactory, + IEnvelopeService envelopeService + ) + { + _serviceProvider = serviceProvider; + _optionsFactory = optionsFactory; + _envelopeService = envelopeService; + } + + public MongoDbSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) + { + return _optionsFactory.Create(snapshotSessionOptionsName); + } + + public async Task CreateSession(MongoDbSnapshotSessionOptions options, + CancellationToken cancellationToken) + { + var mongoClient = new MongoClient(options.ConnectionString); + + var mongoDatabase = mongoClient.GetDatabase(options.DatabaseName); + + var clientSessionHandle = + await mongoClient.StartSessionAsync(new ClientSessionOptions { CausalConsistency = true }, + cancellationToken); + + return MongoSession.Create + ( + _serviceProvider, + mongoDatabase, + clientSessionHandle, + options + ); + } + + public ISnapshotRepository CreateRepository + ( + IMongoSession mongoSession + ) + { + var mongoDbSnapshotRepository = new MongoDbSnapshotRepository + ( + _envelopeService, + mongoSession + ); + + return TryCatchSnapshotRepository.Create(_serviceProvider, mongoDbSnapshotRepository); + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs new file mode 100644 index 00000000..fb63289e --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs @@ -0,0 +1,41 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Common.Disposables; +using EntityDb.MongoDb.Snapshots.Sessions; + +namespace EntityDb.MongoDb.Snapshots; + +internal abstract class MongoDbSnapshotRepositoryFactoryWrapper : DisposableResourceBaseClass, + IMongoDbSnapshotRepositoryFactory +{ + private readonly IMongoDbSnapshotRepositoryFactory _mongoDbSnapshotRepositoryFactory; + + protected MongoDbSnapshotRepositoryFactoryWrapper( + IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory) + { + _mongoDbSnapshotRepositoryFactory = mongoDbSnapshotRepositoryFactory; + } + + public virtual MongoDbSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) + { + return _mongoDbSnapshotRepositoryFactory.GetSessionOptions(snapshotSessionOptionsName); + } + + public virtual Task CreateSession(MongoDbSnapshotSessionOptions options, + CancellationToken cancellationToken) + { + return _mongoDbSnapshotRepositoryFactory.CreateSession(options, cancellationToken); + } + + public virtual ISnapshotRepository CreateRepository + ( + IMongoSession mongoSession + ) + { + return _mongoDbSnapshotRepositoryFactory.CreateRepository(mongoSession); + } + + public override async ValueTask DisposeAsync() + { + await _mongoDbSnapshotRepositoryFactory.DisposeAsync(); + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs new file mode 100644 index 00000000..f7154d16 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.MongoDb.Documents; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Snapshots.Sessions; + +internal interface IMongoSession : IDisposableResource +{ + IMongoDatabase MongoDatabase { get; } + string CollectionName { get; } + + Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken); + + Task Find(Pointer snapshotPointer, CancellationToken cancellationToken); + + Task Delete(Pointer[] snapshotPointers, CancellationToken cancellationToken); + + void StartTransaction(); + Task CommitTransaction(CancellationToken cancellationToken); + Task AbortTransaction(); + + IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options); +} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs new file mode 100644 index 00000000..ff879e72 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs @@ -0,0 +1,49 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.MongoDb.Transactions.Sessions; +using MongoDB.Driver; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.MongoDb.Snapshots.Sessions; + +/// +/// Configuration options for the MongoDb implementation of . +/// +public sealed class MongoDbSnapshotSessionOptions +{ + /// + /// A connection string that is compatible with + /// + public string ConnectionString { get; set; } = default!; + + /// + /// The name of the database that contains the snapshots + /// + public string DatabaseName { get; set; } = default!; + + /// + /// The name of the collection that contains the snapshots + /// + public string CollectionName { get; set; } = default!; + + /// + /// If true, indicates the agent only intends to execute queries. + /// + public bool ReadOnly { get; set; } + + /// + /// If true, indicates the agent can tolerate replication lag for queries. + /// + public bool SecondaryPreferred { get; set; } + + /// + /// Determines how long to wait before a command should be automatically aborted. + /// + public TimeSpan? WriteTimeout { get; set; } + + /// + [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] + public override string ToString() + { + return $"{nameof(MongoDbTransactionSessionOptions)}"; + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs new file mode 100644 index 00000000..eee66539 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs @@ -0,0 +1,261 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Exceptions; +using EntityDb.MongoDb.Documents; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.MongoDb.Snapshots.Sessions; + +internal record MongoSession +( + ILogger Logger, + IMongoDatabase MongoDatabase, + IClientSessionHandle ClientSessionHandle, + MongoDbSnapshotSessionOptions Options +) : DisposableResourceBaseRecord, IMongoSession +{ + public string CollectionName => Options.CollectionName; + + private static readonly WriteConcern WriteConcern = WriteConcern.WMajority; + + private static readonly FilterDefinitionBuilder Filter = Builders.Filter; + + private static readonly ReplaceOptions UpsertOptions = new() { IsUpsert = true }; + + private static FilterDefinition GetFilter(Id pointerId, VersionNumber pointerVersionNumber) + { + return Filter.And + ( + Filter.Eq(document => document.PointerId, pointerId), + Filter.Eq(document => document.PointerVersionNumber, pointerVersionNumber) + ); + } + + public async Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken) + { + AssertNotReadOnly(); + + var serverSessionId = ClientSessionHandle.ServerSession.Id.ToString(); + + Logger + .LogInformation + ( + "Started Running MongoDb Upsert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId + ); + + await MongoDatabase + .GetCollection(Options.CollectionName) + .ReplaceOneAsync + ( + ClientSessionHandle, + GetFilter(snapshotDocument.PointerId, snapshotDocument.PointerVersionNumber), + snapshotDocument, + UpsertOptions, + cancellationToken: cancellationToken + ); + + Logger + .LogInformation + ( + "Finished Running MongoDb Upsert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId + ); + } + + public async Task Find + ( + Pointer snapshotPointer, + CancellationToken cancellationToken + ) + { + var filter = GetFilter(snapshotPointer.Id, snapshotPointer.VersionNumber); + + var find = MongoDatabase + .GetCollection(Options.CollectionName) + .WithReadPreference(GetReadPreference()) + .WithReadConcern(GetReadConcern()) + .Find(ClientSessionHandle, filter); + + var query = find.ToString(); + var serverSessionId = ClientSessionHandle.ServerSession.Id.ToString(); + + Logger + .LogInformation + ( + "Started MongoDb Query on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}\n\nQuery: {Query}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId, + query + ); + + var snapshotDocument = await find.SingleOrDefaultAsync(cancellationToken); + + Logger + .LogInformation + ( + "Finished MongoDb Query on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}\n\nQuery: {Query}\n\nDocument Returned: {DocumentReturned}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId, + query, + snapshotDocument != null + ); + + return snapshotDocument; + } + + public async Task Delete(Pointer[] snapshotPointers, CancellationToken cancellationToken) + { + AssertNotReadOnly(); + + var filter = Filter.And(snapshotPointers.Select(pointer => GetFilter(pointer.Id, pointer.VersionNumber))); + + var serverSessionId = ClientSessionHandle.ServerSession.Id.ToString(); + + var command = MongoDatabase + .GetCollection(Options.CollectionName) + .Find(filter) + .ToString()!.Replace("find", "deleteMany"); + + Logger + .LogInformation + ( + "Started Running MongoDb Delete on `{DatabaseNamespace}.{CollectionName}`\n\nServer SessionId: {ServerSessionId}\n\nCommand: {Command}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId, + command + ); + + var deleteResult = await MongoDatabase + .GetCollection(Options.CollectionName) + .DeleteManyAsync + ( + ClientSessionHandle, + filter, + cancellationToken: cancellationToken + ); + + Logger + .LogInformation( + "Finished Running MongoDb Delete on `{DatabaseNamespace}.{CollectionName}`\n\nServer SessionId: {ServerSessionId}\n\nCommand: {Command}\n\nDocuments Deleted: {DocumentsDeleted}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId, + command, + deleteResult.IsAcknowledged ? "(Not Available)" : deleteResult.DeletedCount + ); + } + + public IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options) + { + return this with { Options = options }; + } + + public void StartTransaction() + { + AssertNotReadOnly(); + + ClientSessionHandle.StartTransaction(new TransactionOptions + ( + writeConcern: WriteConcern, + maxCommitTime: Options.WriteTimeout + )); + + Logger + .LogInformation + ( + "Started MongoDb Transaction on `{DatabaseNamespace}`\n\nServer SessionId: {ServerSessionId}", + MongoDatabase.DatabaseNamespace, + ClientSessionHandle.ServerSession.Id.ToString() + ); + } + + [ExcludeFromCodeCoverage(Justification = + "Tests should run with the Debug configuration, and should not execute this method.")] + public async Task CommitTransaction(CancellationToken cancellationToken) + { + AssertNotReadOnly(); + + await ClientSessionHandle.CommitTransactionAsync(cancellationToken); + + Logger + .LogInformation + ( + "Committed MongoDb Transaction on `{DatabaseNamespace}`\n\nServer SessionId: {ServerSessionId}", + MongoDatabase.DatabaseNamespace, + ClientSessionHandle.ServerSession.Id.ToString() + ); + } + + public async Task AbortTransaction() + { + AssertNotReadOnly(); + + await ClientSessionHandle.AbortTransactionAsync(); + + Logger + .LogInformation + ( + "Aborted MongoDb Transaction on `{DatabaseNamespace}`\n\nServer SessionId: {ServerSessionId}", + MongoDatabase.DatabaseNamespace, + ClientSessionHandle.ServerSession.Id.ToString() + ); + } + + public override ValueTask DisposeAsync() + { + ClientSessionHandle.Dispose(); + + return base.DisposeAsync(); + } + + private ReadPreference GetReadPreference() + { + if (!Options.ReadOnly) + { + return ReadPreference.Primary; + } + + return Options.SecondaryPreferred + ? ReadPreference.SecondaryPreferred + : ReadPreference.PrimaryPreferred; + } + + [ExcludeFromCodeCoverage(Justification = "Tests should always run in a transaction.")] + private ReadConcern GetReadConcern() + { + return ClientSessionHandle.IsInTransaction + ? ReadConcern.Snapshot + : ReadConcern.Majority; + } + + private void AssertNotReadOnly() + { + if (Options.ReadOnly) + { + throw new CannotWriteInReadOnlyModeException(); + } + } + + public static IMongoSession Create + ( + IServiceProvider serviceProvider, + IMongoDatabase mongoDatabase, + IClientSessionHandle clientSessionHandle, + MongoDbSnapshotSessionOptions options + ) + { + return ActivatorUtilities.CreateInstance(serviceProvider, mongoDatabase, clientSessionHandle, + options); + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs new file mode 100644 index 00000000..3e46aee3 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs @@ -0,0 +1,54 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.MongoDb.Documents; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Snapshots.Sessions; + +internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession +{ + public string CollectionName => MongoSession.CollectionName; + + public IMongoDatabase MongoDatabase => MongoSession.MongoDatabase; + + public Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken) + { + return MongoSession.Upsert(snapshotDocument, cancellationToken); + } + + public Task Find(Pointer snapshotPointer, CancellationToken cancellationToken) + { + return MongoSession.Find + ( + snapshotPointer, + cancellationToken + ); + } + + public Task Delete(Pointer[] snapshotPointers, CancellationToken cancellationToken) + { + return MongoSession.Delete(snapshotPointers, cancellationToken); + } + + public IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options) + { + return this with { MongoSession = MongoSession.WithSessionOptions(options) }; + } + + public void StartTransaction() + { + // Test Mode Snapshots are started in the Test Mode Repository Factory + } + + public Task CommitTransaction(CancellationToken cancellationToken) + { + // Test Mode Snapshots are never committed + return Task.CompletedTask; + } + + public Task AbortTransaction() + { + // Test Mode Snapshots are aborted in the Test Mode Repository Factory + return Task.CompletedTask; + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs new file mode 100644 index 00000000..4bcf72b0 --- /dev/null +++ b/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs @@ -0,0 +1,52 @@ +using EntityDb.MongoDb.Snapshots.Sessions; + +namespace EntityDb.MongoDb.Snapshots; + +internal class TestModeMongoDbSnapshotRepositoryFactory : MongoDbSnapshotRepositoryFactoryWrapper +{ + private (IMongoSession Normal, TestModeMongoSession TestMode)? _sessions; + + public TestModeMongoDbSnapshotRepositoryFactory + ( + IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory + ) + : base(mongoDbSnapshotRepositoryFactory) + { + } + + public override async Task CreateSession(MongoDbSnapshotSessionOptions options, + CancellationToken cancellationToken) + { + if (_sessions.HasValue) + { + return _sessions.Value.TestMode + .WithSessionOptions(options); + } + + var normalOptions = new MongoDbSnapshotSessionOptions + { + ConnectionString = options.ConnectionString, + DatabaseName = options.DatabaseName, + }; + + var normalSession = await base.CreateSession(normalOptions, cancellationToken); + + var testModeSession = new TestModeMongoSession(normalSession); + + normalSession.StartTransaction(); + + _sessions = (normalSession, testModeSession); + + return _sessions.Value.TestMode + .WithSessionOptions(options); + } + + public override async ValueTask DisposeAsync() + { + if (_sessions.HasValue) + { + await _sessions.Value.Normal.AbortTransaction(); + await _sessions.Value.Normal.DisposeAsync(); + } + } +} diff --git a/src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs index 61c58ecf..14857756 100644 --- a/src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs @@ -1,5 +1,5 @@ using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -52,7 +52,7 @@ public override async Task CreateSession(MongoDbTransactionSessio return mongoSession; } - await mongoSession.MongoDatabase.Client.ProvisionCollections(mongoSession.MongoDatabase.DatabaseNamespace + await mongoSession.MongoDatabase.Client.ProvisionTransactionCollections(mongoSession.MongoDatabase.DatabaseNamespace .DatabaseName, cancellationToken); _provisioned = true; diff --git a/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs index a1664268..144a7f35 100644 --- a/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs @@ -1,5 +1,5 @@ using EntityDb.Abstractions.Transactions; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; namespace EntityDb.MongoDb.Transactions; diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs index aa6ace76..036493ec 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs @@ -10,7 +10,7 @@ using EntityDb.Common.Exceptions; using EntityDb.MongoDb.Documents; using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using MongoDB.Bson; namespace EntityDb.MongoDb.Transactions; diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs index 3169d8fd..475fd1a9 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs @@ -2,7 +2,7 @@ using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; using EntityDb.Common.Transactions; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; @@ -43,8 +43,6 @@ public async Task CreateSession(MongoDbTransactionSessionOptions await mongoClient.StartSessionAsync(new ClientSessionOptions { CausalConsistency = true }, cancellationToken); - - return MongoSession.Create ( _serviceProvider, diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs index d09474c8..a847c4fb 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Transactions; using EntityDb.Common.Disposables; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; namespace EntityDb.MongoDb.Transactions; diff --git a/src/EntityDb.MongoDb/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/Transactions/Sessions/IMongoSession.cs similarity index 92% rename from src/EntityDb.MongoDb/Sessions/IMongoSession.cs rename to src/EntityDb.MongoDb/Transactions/Sessions/IMongoSession.cs index aed64aa4..3280bb92 100644 --- a/src/EntityDb.MongoDb/Sessions/IMongoSession.cs +++ b/src/EntityDb.MongoDb/Transactions/Sessions/IMongoSession.cs @@ -3,7 +3,7 @@ using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Sessions; +namespace EntityDb.MongoDb.Transactions.Sessions; internal interface IMongoSession : IDisposableResource { diff --git a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/Transactions/Sessions/MongoDbTransactionSessionOptions.cs similarity index 96% rename from src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs rename to src/EntityDb.MongoDb/Transactions/Sessions/MongoDbTransactionSessionOptions.cs index bf6b5f9f..67a53174 100644 --- a/src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs +++ b/src/EntityDb.MongoDb/Transactions/Sessions/MongoDbTransactionSessionOptions.cs @@ -2,7 +2,7 @@ using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.MongoDb.Sessions; +namespace EntityDb.MongoDb.Transactions.Sessions; /// /// Configuration options for the MongoDb implementation of . diff --git a/src/EntityDb.MongoDb/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Transactions/Sessions/MongoSession.cs similarity index 96% rename from src/EntityDb.MongoDb/Sessions/MongoSession.cs rename to src/EntityDb.MongoDb/Transactions/Sessions/MongoSession.cs index 257a8cd9..78cf6d4d 100644 --- a/src/EntityDb.MongoDb/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/Transactions/Sessions/MongoSession.cs @@ -8,7 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace EntityDb.MongoDb.Sessions; +namespace EntityDb.MongoDb.Transactions.Sessions; internal record MongoSession ( diff --git a/src/EntityDb.MongoDb/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Transactions/Sessions/TestModeMongoSession.cs similarity index 94% rename from src/EntityDb.MongoDb/Sessions/TestModeMongoSession.cs rename to src/EntityDb.MongoDb/Transactions/Sessions/TestModeMongoSession.cs index 62269fe5..c3bdba04 100644 --- a/src/EntityDb.MongoDb/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/Transactions/Sessions/TestModeMongoSession.cs @@ -3,7 +3,7 @@ using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Sessions; +namespace EntityDb.MongoDb.Transactions.Sessions; internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession { diff --git a/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs index feaf17a9..b1088851 100644 --- a/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs @@ -1,4 +1,4 @@ -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; namespace EntityDb.MongoDb.Transactions; diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs index 23a1e576..cbf8ecf9 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs @@ -39,7 +39,7 @@ private static async Task Execute(Arguments arguments) new MongoClient( $"{expectedProtocol}{arguments.ServiceName}:{arguments.ServicePassword}@{cluster.SrvAddress[expectedProtocol.Length..]}/admin"); - await mongoClient.ProvisionCollections(arguments.ServiceName); + await mongoClient.ProvisionTransactionCollections(arguments.ServiceName); } protected static void AddClusterNameArgument(Command command) diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs index 6a6b7ba0..83df1ff9 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs @@ -39,7 +39,7 @@ private static async Task Execute(Arguments arguments) new MongoClient( $"{expectedProtocol}{arguments.ServiceName}:{arguments.ServicePassword}@{serverlessInstance.ConnectionStrings.StandardSrv[expectedProtocol.Length..]}/admin"); - await mongoClient.ProvisionCollections(arguments.ServiceName); + await mongoClient.ProvisionTransactionCollections(arguments.ServiceName); } protected static void AddServerlessNameArgument(Command command) diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs index 3cdc87a3..46500f08 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs @@ -17,7 +17,7 @@ private static async Task Execute(Arguments arguments) { var mongoClient = new MongoClient(arguments.ConnectionString); - await mongoClient.ProvisionCollections(arguments.ServiceName); + await mongoClient.ProvisionTransactionCollections(arguments.ServiceName); } private static void AddConnectionStringArgument(Command command) diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index f8c63e81..bce362fb 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -65,6 +65,7 @@ public bool ShouldRecordAsLatest(TestEntity? previousMostRecentSnapshot) return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousMostRecentSnapshot); } + public static string MongoDbCollectionName => "TestEntities"; public static string RedisKeyNamespace => "test-entity"; public TestEntity WithVersionNumber(VersionNumber versionNumber) diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index c5bdfd4d..cfb01a61 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -119,6 +119,7 @@ public static IEnumerable EnumerateProjectionIds(ISource source) return projectionIds; } + public static string MongoDbCollectionName => "OneToOneProjections"; public static string RedisKeyNamespace => "one-to-one-projection"; public OneToOneProjection WithVersionNumber(VersionNumber versionNumber) diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs index 72b2a309..81e4ef63 100644 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs @@ -9,6 +9,7 @@ public interface ISnapshotWithTestLogic : ISnapshot, IEnti where TSnapshot : class { VersionNumber VersionNumber { get; } + static abstract string MongoDbCollectionName { get; } static abstract string RedisKeyNamespace { get; } static abstract AsyncLocal?> ShouldRecordLogic { get; } static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } diff --git a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs index ec1523c6..3b774941 100644 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs @@ -33,7 +33,7 @@ private async Task var snapshotId = Id.NewId(); var expectedSnapshot = TSnapshot.Construct(snapshotId).WithVersionNumber(new VersionNumber(300)); - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider + var snapshotRepositoryFactory = serviceScope.ServiceProvider .GetRequiredService>(); await using var snapshotRepository = await snapshotRepositoryFactory diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index e0117aee..f616eecd 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -16,7 +16,8 @@ using EntityDb.EntityFramework.Sessions; using EntityDb.MongoDb.Extensions; using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Snapshots.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using EntityDb.Npgsql.Extensions; using EntityDb.Npgsql.Queries; using EntityDb.Redis.Extensions; @@ -40,7 +41,7 @@ public class TestsBase where TStartup : IStartup, new() { public delegate void AddDependenciesDelegate(IServiceCollection serviceCollection); - + private static readonly TransactionsAdder[] AllTransactionAdders = { new("MongoDb", typeof(MongoDbQueryOptions), (timeStamp) => timeStamp.WithMillisecondPrecision(), serviceCollection => @@ -92,31 +93,31 @@ public class TestsBase var databaseContainerFixture = serviceCollection .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) .ImplementationInstance as DatabaseContainerFixture; - + serviceCollection.AddNpgsqlTransactions(true, true); - + serviceCollection.Configure("Count", options => { options.LeaseValueSortCollation = "numeric"; options.TagValueSortCollation = "numeric"; }); - + serviceCollection.ConfigureAll(options => { options.ConnectionString = $"{databaseContainerFixture!.PostgreSqlContainer.GetConnectionString()};Include Error Detail=true"; }); - + serviceCollection.Configure(TestSessionOptions.Write, options => { options.ReadOnly = false; }); - + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { options.ReadOnly = true; options.SecondaryPreferred = false; }); - + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => { options.ReadOnly = true; @@ -186,6 +187,47 @@ private static SnapshotAdder EntityFrameworkSnapshotAdder() }); }); } + + private static SnapshotAdder MongoDbSnapshotAdder() + where TSnapshot : class, ISnapshotWithTestLogic + { + return new SnapshotAdder($"MongoDb<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => + { + var databaseContainerFixture = serviceCollection + .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) + .ImplementationInstance as DatabaseContainerFixture; + + serviceCollection.AddMongoDbSnapshots(true, true); + + serviceCollection.ConfigureAll(options => + { + var host = databaseContainerFixture!.MongoDbContainer.Hostname; + var port = databaseContainerFixture!.MongoDbContainer.GetMappedPublicPort(27017); + + options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); + options.DatabaseName = DatabaseContainerFixture.OmniParameter; + options.CollectionName = TSnapshot.MongoDbCollectionName; + options.WriteTimeout = TimeSpan.FromSeconds(1); + }); + + serviceCollection.Configure(TestSessionOptions.Write, options => + { + options.ReadOnly = false; + }); + + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => + { + options.ReadOnly = true; + options.SecondaryPreferred = false; + }); + + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => + { + options.ReadOnly = true; + options.SecondaryPreferred = true; + }); + }); + } private static SnapshotAdder RedisSnapshotAdder() where TSnapshot : class, ISnapshotWithTestLogic @@ -228,6 +270,7 @@ private static IEnumerable AllSnapshotAdders() { yield return EntityFrameworkSnapshotAdder(); yield return RedisSnapshotAdder(); + yield return MongoDbSnapshotAdder(); } private static EntityAdder GetEntityAdder() diff --git a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs index c1d593e6..f96c88f8 100644 --- a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs +++ b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs @@ -1,6 +1,6 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Transactions.Sessions; using Shouldly; using Xunit; From 74394d762065342ded4114bee6a66f3f235fc391 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 24 Dec 2023 23:31:25 -0800 Subject: [PATCH 59/93] refactor: Lease & Tag Commands can have access to Entity Id / Version Number --- src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs | 7 ++++++- src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs | 7 ++++++- .../Commands/IDeleteLeasesCommand.cs | 7 ++++++- src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs | 7 ++++++- src/EntityDb.MongoDb/Documents/LeaseDocument.cs | 4 ++-- src/EntityDb.MongoDb/Documents/TagDocument.cs | 4 ++-- src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs | 4 ++-- src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs | 4 ++-- .../Implementations/Commands/DoNothing.cs | 9 +++++---- .../Implementations/Commands/StoreNumber.cs | 5 +++-- .../Transactions/SingleEntityTransactionBuilderTests.cs | 2 +- 11 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs index 1efce3a1..29b3f255 100644 --- a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; @@ -11,5 +12,9 @@ public interface IAddLeasesCommand /// Returns the leases that need to be added. /// /// The leases that need to be added. - IEnumerable GetLeases(); + IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber); + + /// + [Obsolete("Please us GetLeases(Id, VersionNumber) instead. This will be removed in a future version.", true)] + IEnumerable GetLeases() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs index 5001ecb8..94eb42a9 100644 --- a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; @@ -11,5 +12,9 @@ public interface IAddTagsCommand /// Returns the tags that need to be added. /// /// The tags that need to be added. - IEnumerable GetTags(); + IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber); + + /// + [Obsolete("Please use GetTags(Id, VersionNumber) instead. This will be removed in a future version.", true)] + IEnumerable GetTags() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs index 301cf009..b230db86 100644 --- a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; @@ -11,5 +12,9 @@ public interface IDeleteLeasesCommand /// Returns the leases that need to be deleted. /// /// The leases that need to be deleted. - IEnumerable GetLeases(); + IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber); + + /// + [Obsolete("Please us GetLeases(Id, VersionNumber) instead. This will be removed in a future version.", true)] + IEnumerable GetLeases() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs index bf760d07..088f4e8e 100644 --- a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; @@ -11,5 +12,9 @@ public interface IDeleteTagsCommand /// Returns the tags that need to be deleted. /// /// The tags that need to be deleted. - IEnumerable GetTags(); + IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber); + + /// + [Obsolete("Please use GetTags(Id, VersionNumber) instead. This will be removed in a future version,", true)] + IEnumerable GetTags() => throw new NotImplementedException(); } diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs index 204fd403..521358dc 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs @@ -34,7 +34,7 @@ public static InsertDocumentsCommand GetInsertCommand IAddLeasesCommand addLeasesCommand ) { - var leaseDocuments = addLeasesCommand.GetLeases() + var leaseDocuments = addLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) .Select(insertLease => new LeaseDocument { TransactionTimeStamp = transaction.TimeStamp, @@ -79,7 +79,7 @@ IDeleteLeasesCommand deleteLeasesCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases().ToArray()); + new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDocument.cs index 51713809..e95b7882 100644 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDocument.cs @@ -33,7 +33,7 @@ public static InsertDocumentsCommand GetInsertCommand IAddTagsCommand addTagsCommand ) { - var tagDocuments = addTagsCommand.GetTags() + var tagDocuments = addTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) .Select(insertTag => new TagDocument { TransactionTimeStamp = transaction.TimeStamp, @@ -76,7 +76,7 @@ public static DeleteDocumentsCommand GetDeleteCommand IDeleteTagsCommand deleteTagsCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags().ToArray()); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs index 6e2ea910..9d87e4b3 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs @@ -44,7 +44,7 @@ IAddLeasesCommand addLeasesCommand return new InsertDocumentsCommand ( TableName, - addLeasesCommand.GetLeases() + addLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) .Select(insertLease => new LeaseDocument { TransactionTimeStamp = transaction.TimeStamp, @@ -82,7 +82,7 @@ public static DeleteDocumentsCommand GetDeleteCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases().ToArray()); + new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs index 45c87c09..eaf71f3e 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs @@ -43,7 +43,7 @@ IAddTagsCommand addTagsCommand return new InsertDocumentsCommand ( TableName, - addTagsCommand.GetTags() + addTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) .Select(insertTag => new TagDocument { TransactionTimeStamp = transaction.TimeStamp, @@ -79,7 +79,7 @@ public static DeleteDocumentsCommand GetDeleteCommand ITransactionCommand transactionCommand, IDeleteTagsCommand deleteTagsCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags().ToArray()); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); return new DeleteDocumentsCommand ( diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs index 70dca995..9a1586cf 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs @@ -2,6 +2,7 @@ using EntityDb.Abstractions.Leases; using EntityDb.Abstractions.States; using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; @@ -22,7 +23,7 @@ public TestEntity Reduce(TestEntity entity) public record AddLease(ILease Lease) : DoNothing, IAddLeasesCommand { - public IEnumerable GetLeases() + public IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber) { yield return Lease; } @@ -30,7 +31,7 @@ public IEnumerable GetLeases() public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesCommand { - public IEnumerable GetLeases() + public IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber) { yield return Lease; } @@ -38,7 +39,7 @@ public IEnumerable GetLeases() public record AddTag(ITag Tag) : DoNothing, IAddTagsCommand { - public IEnumerable GetTags() + public IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber) { yield return Tag; } @@ -46,7 +47,7 @@ public IEnumerable GetTags() public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsCommand { - public IEnumerable GetTags() + public IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber) { yield return Tag; } diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs index 632dc94d..6773db22 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs @@ -2,6 +2,7 @@ using EntityDb.Abstractions.Leases; using EntityDb.Abstractions.States; using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Leases; using EntityDb.Common.Tests.Implementations.Projections; @@ -24,12 +25,12 @@ public TestEntity Reduce(TestEntity entity) }; } - public IEnumerable GetLeases() + public IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber) { yield return new CountLease(Number); } - public IEnumerable GetTags() + public IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber) { yield return new CountTag(Number); } diff --git a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs index 6d314309..df4fa1a5 100644 --- a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs @@ -105,7 +105,7 @@ private async Task var addLeasesCommand = transaction.Commands[0].Data.ShouldBeAssignableTo().ShouldNotBeNull(); - addLeasesCommand.GetLeases().ShouldNotBeEmpty(); + addLeasesCommand.GetLeases(default, default).ShouldNotBeEmpty(); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( From d9de3fc0d60bf54137f7fda7c77e5a23892f6db7 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Mon, 25 Dec 2023 00:18:29 -0800 Subject: [PATCH 60/93] refactor: better refactor of Add/Delete Tags/Leases pass in the whole entity, move responsibility of checking for implementation to the transaction builder, instead of repeating it in every single TransactionRepository implementation --- .../Commands/IAddLeasesCommand.cs | 13 +++++++--- .../Commands/IAddTagsCommand.cs | 13 +++++++--- .../Commands/IDeleteLeasesCommand.cs | 14 ++++++++--- .../Commands/IDeleteTagsCommand.cs | 13 +++++++--- .../Transactions/ITransactionCommand.cs | 25 ++++++++++++++++++- .../TransactionRepositoryExtensions.cs | 15 +++++++++-- .../Builders/TransactionBuilder.cs | 15 +++++++++++ .../Transactions/TransactionCommand.cs | 13 +++++++--- .../Documents/LeaseDocument.cs | 22 ++++++++-------- src/EntityDb.MongoDb/Documents/TagDocument.cs | 20 +++++++-------- .../MongoDbTransactionRepository.cs | 16 ++++++------ .../Documents/Lease/LeaseDocument.cs | 21 ++++++++-------- .../Documents/Tag/TagDocument.cs | 19 +++++++------- .../SqlDbTransactionRepository.cs | 16 ++++++------ .../Implementations/Commands/DoNothing.cs | 16 ++++++------ .../Implementations/Commands/StoreNumber.cs | 6 ++--- .../Seeders/TransactionCommandSeeder.cs | 11 ++++++-- .../SingleEntityTransactionBuilderTests.cs | 6 +++-- 18 files changed, 177 insertions(+), 97 deletions(-) diff --git a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs index 29b3f255..7e12a7d6 100644 --- a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs @@ -1,20 +1,25 @@ using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; /// /// Represents a command that adds leases. /// -public interface IAddLeasesCommand +/// The type of the entity. +public interface IAddLeasesCommand { /// /// Returns the leases that need to be added. /// + /// The entity for which leases will be added. /// The leases that need to be added. - IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber); + IEnumerable GetLeases(TEntity entity); +} +/// +[Obsolete("Please use IAddLeasesCommand instead. This will be removed in a future version.", true)] +public interface IAddLeasesCommand +{ /// - [Obsolete("Please us GetLeases(Id, VersionNumber) instead. This will be removed in a future version.", true)] IEnumerable GetLeases() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs index 94eb42a9..923777e0 100644 --- a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs @@ -1,20 +1,25 @@ using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; /// /// Represents a command that adds tags. /// -public interface IAddTagsCommand +/// The type of the entity +public interface IAddTagsCommand { /// /// Returns the tags that need to be added. /// + /// The entity for which tags will be added /// The tags that need to be added. - IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber); + IEnumerable GetTags(TEntity entity); +} +/// +[Obsolete("Please use IAddTagsCommand instead. This will be removed in a future version.", true)] +public interface IAddTagsCommand +{ /// - [Obsolete("Please use GetTags(Id, VersionNumber) instead. This will be removed in a future version.", true)] IEnumerable GetTags() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs index b230db86..9c1e3cc1 100644 --- a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs @@ -6,15 +6,21 @@ namespace EntityDb.Abstractions.Commands; /// /// Represents a command that deletes leases. /// -public interface IDeleteLeasesCommand +/// The type of the entity +public interface IDeleteLeasesCommand { /// /// Returns the leases that need to be deleted. /// + /// The entity for which leases will be removed. /// The leases that need to be deleted. - IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber); - + IEnumerable GetLeases(TEntity entity); +} + +/// +[Obsolete("Please use IDeleteLeasesCommand instead. This will be removed in a future version.", true)] +public interface IDeleteLeasesCommand +{ /// - [Obsolete("Please us GetLeases(Id, VersionNumber) instead. This will be removed in a future version.", true)] IEnumerable GetLeases() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs index 088f4e8e..dd4fc47f 100644 --- a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs +++ b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs @@ -1,20 +1,25 @@ using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Commands; /// /// Represents a command that deletes tags. /// -public interface IDeleteTagsCommand +/// The type of the entity. +public interface IDeleteTagsCommand { /// /// Returns the tags that need to be deleted. /// + /// The entity for which tags will be deleted. /// The tags that need to be deleted. - IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber); + IEnumerable GetTags(TEntity entity); +} +/// +[Obsolete("Please use IDeleteTagsCommand instead. This will be removed in a future version,", true)] +public interface IDeleteTagsCommand +{ /// - [Obsolete("Please use GetTags(Id, VersionNumber) instead. This will be removed in a future version,", true)] IEnumerable GetTags() => throw new NotImplementedException(); } diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs index eb82ba3e..78c5faa6 100644 --- a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs +++ b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs @@ -1,4 +1,7 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.ValueObjects; +using System.Collections.Immutable; namespace EntityDb.Abstractions.Transactions; @@ -21,6 +24,26 @@ public interface ITransactionCommand /// The command data. /// object Data { get; } + + /// + /// The leases to be added. + /// + ImmutableArray AddLeases { get; } + + /// + /// The tags to be added. + /// + ImmutableArray AddTags { get; } + + /// + /// The leases to be deleted. + /// + ImmutableArray DeleteLeases { get; } + + /// + /// The tags to be deleted. + /// + ImmutableArray DeleteTags { get; } /// [Obsolete("Please use Data instead. This will be removed in a future version.")] diff --git a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs index 6c92c5cd..b9c88eeb 100644 --- a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs +++ b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs @@ -1,4 +1,7 @@ -using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Commands; +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Queries; @@ -38,6 +41,10 @@ public static IAsyncEnumerable EnumerateTransactionIds(this ITransactionRepo /// The transaction id /// A cancellation token /// An instance of . + /// + /// This does *not* initialize , , + /// , nor . They will be empty. + /// public static async Task GetTransaction(this ITransactionRepository transactionRepository, Id transactionId, CancellationToken cancellationToken) { var query = new GetTransactionCommandsQuery(transactionId); @@ -52,7 +59,11 @@ public static async Task GetTransaction(this ITransactionRepositor { EntityId = annotatedCommand.EntityId, EntityVersionNumber = annotatedCommand.EntityVersionNumber, - Data = annotatedCommand.Data + Data = annotatedCommand.Data, + AddLeases = ImmutableArray.Empty, + AddTags = ImmutableArray.Empty, + DeleteLeases = ImmutableArray.Empty, + DeleteTags = ImmutableArray.Empty, }) .ToArrayAsync(cancellationToken); diff --git a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs index ab604e22..77a4f691 100644 --- a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs +++ b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs @@ -1,4 +1,7 @@ using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Commands; +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Builders; using EntityDb.Abstractions.ValueObjects; @@ -54,6 +57,18 @@ public ITransactionBuilder Append(Id entityId, object command) EntityId = entityId, EntityVersionNumber = entityVersionNumber, Data = command, + AddLeases = command is IAddLeasesCommand addLeasesCommand + ? addLeasesCommand.GetLeases(entity).ToImmutableArray() + : ImmutableArray.Empty, + AddTags = command is IAddTagsCommand addTagsCommand + ? addTagsCommand.GetTags(entity).ToImmutableArray() + : ImmutableArray.Empty, + DeleteLeases = command is IDeleteLeasesCommand deleteLeasesCommand + ? deleteLeasesCommand.GetLeases(entity).ToImmutableArray() + : ImmutableArray.Empty, + DeleteTags = command is IDeleteTagsCommand deleteTagsCommand + ? deleteTagsCommand.GetTags(entity).ToImmutableArray() + : ImmutableArray.Empty, }); _knownEntities[entityId] = entity; diff --git a/src/EntityDb.Common/Transactions/TransactionCommand.cs b/src/EntityDb.Common/Transactions/TransactionCommand.cs index fb50eaa2..b835d73b 100644 --- a/src/EntityDb.Common/Transactions/TransactionCommand.cs +++ b/src/EntityDb.Common/Transactions/TransactionCommand.cs @@ -1,11 +1,18 @@ +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Tags; using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; +using System.Collections.Immutable; namespace EntityDb.Common.Transactions; internal record TransactionCommand : ITransactionCommand { - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } - public object Data { get; init; } = default!; + public required Id EntityId { get; init; } + public required VersionNumber EntityVersionNumber { get; init; } + public required object Data { get; init; } = default!; + public required ImmutableArray AddLeases { get; init; } + public required ImmutableArray AddTags { get; init; } + public required ImmutableArray DeleteLeases { get; init; } + public required ImmutableArray DeleteTags { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs index 521358dc..b7aee044 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs @@ -30,22 +30,21 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - ITransactionCommand transactionCommand, - IAddLeasesCommand addLeasesCommand + ITransactionCommand transactionCommand ) { - var leaseDocuments = addLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) - .Select(insertLease => new LeaseDocument + var leaseDocuments = transactionCommand.AddLeases + .Select(lease => new LeaseDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, EntityId = transactionCommand.EntityId, EntityVersionNumber = transactionCommand.EntityVersionNumber, - DataType = insertLease.GetType().Name, - Data = envelopeService.Serialize(insertLease), - Scope = insertLease.Scope, - Label = insertLease.Label, - Value = insertLease.Value + DataType = lease.GetType().Name, + Data = envelopeService.Serialize(lease), + Scope = lease.Scope, + Label = lease.Label, + Value = lease.Value }) .ToArray(); @@ -74,12 +73,11 @@ leaseQuery.Options as MongoDbQueryOptions public static DeleteDocumentsCommand GetDeleteCommand ( - ITransactionCommand transactionCommand, - IDeleteLeasesCommand deleteLeasesCommand + ITransactionCommand transactionCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); + new DeleteLeasesQuery(transactionCommand.EntityId, transactionCommand.DeleteLeases); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDocument.cs index e95b7882..11fb95b4 100644 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDocument.cs @@ -29,21 +29,20 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - ITransactionCommand transactionCommand, - IAddTagsCommand addTagsCommand + ITransactionCommand transactionCommand ) { - var tagDocuments = addTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) - .Select(insertTag => new TagDocument + var tagDocuments = transactionCommand.AddTags + .Select(tag => new TagDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, EntityId = transactionCommand.EntityId, EntityVersionNumber = transactionCommand.EntityVersionNumber, - DataType = insertTag.GetType().Name, - Data = envelopeService.Serialize(insertTag), - Label = insertTag.Label, - Value = insertTag.Value + DataType = tag.GetType().Name, + Data = envelopeService.Serialize(tag), + Label = tag.Label, + Value = tag.Value }) .ToArray(); @@ -72,11 +71,10 @@ tagQuery.Options as MongoDbQueryOptions public static DeleteDocumentsCommand GetDeleteCommand ( - ITransactionCommand transactionCommand, - IDeleteTagsCommand deleteTagsCommand + ITransactionCommand transactionCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, transactionCommand.DeleteTags); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs index 036493ec..fb36ccc6 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs +++ b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs @@ -198,31 +198,31 @@ await CommandDocument .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_mongoSession, cancellationToken); - if (transactionCommand.Data is IAddLeasesCommand addLeasesCommand) + if (transactionCommand.AddLeases.Length > 0) { await LeaseDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand, addLeasesCommand) + .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_mongoSession, cancellationToken); } - if (transactionCommand.Data is IAddTagsCommand addTagsCommand) + if (transactionCommand.AddTags.Length > 0) { await TagDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand, addTagsCommand) + .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_mongoSession, cancellationToken); } - if (transactionCommand.Data is IDeleteLeasesCommand deleteLeasesCommand) + if (transactionCommand.DeleteLeases.Length > 0) { await LeaseDocument - .GetDeleteCommand(transactionCommand, deleteLeasesCommand) + .GetDeleteCommand(transactionCommand) .Execute(_mongoSession, cancellationToken); } - if (transactionCommand.Data is IDeleteTagsCommand deleteTagsCommand) + if (transactionCommand.DeleteTags.Length > 0) { await TagDocument - .GetDeleteCommand(transactionCommand, deleteTagsCommand) + .GetDeleteCommand(transactionCommand) .Execute(_mongoSession, cancellationToken); } } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs index 9d87e4b3..3fb8bb32 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs @@ -37,25 +37,24 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - ITransactionCommand transactionCommand, - IAddLeasesCommand addLeasesCommand + ITransactionCommand transactionCommand ) { return new InsertDocumentsCommand ( TableName, - addLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) - .Select(insertLease => new LeaseDocument + transactionCommand.AddLeases + .Select(lease => new LeaseDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, EntityId = transactionCommand.EntityId, EntityVersionNumber = transactionCommand.EntityVersionNumber, - Scope = insertLease.Scope, - Label = insertLease.Label, - Value = insertLease.Value, - DataType = insertLease.GetType().Name, - Data = envelopeService.Serialize(insertLease) + Scope = lease.Scope, + Label = lease.Label, + Value = lease.Value, + DataType = lease.GetType().Name, + Data = envelopeService.Serialize(lease) }) .ToArray() ); @@ -78,11 +77,11 @@ ILeaseQuery leaseQuery public static DeleteDocumentsCommand GetDeleteCommand ( - ITransactionCommand transactionCommand, IDeleteLeasesCommand deleteLeasesCommand + ITransactionCommand transactionCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.EntityId, deleteLeasesCommand.GetLeases(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); + new DeleteLeasesQuery(transactionCommand.EntityId, transactionCommand.DeleteLeases); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs index eaf71f3e..046cbe9f 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs @@ -36,24 +36,23 @@ public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, ITransaction transaction, - ITransactionCommand transactionCommand, - IAddTagsCommand addTagsCommand + ITransactionCommand transactionCommand ) { return new InsertDocumentsCommand ( TableName, - addTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber) - .Select(insertTag => new TagDocument + transactionCommand.AddTags + .Select(tag => new TagDocument { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, EntityId = transactionCommand.EntityId, EntityVersionNumber = transactionCommand.EntityVersionNumber, - Label = insertTag.Label, - Value = insertTag.Value, - DataType = insertTag.GetType().Name, - Data = envelopeService.Serialize(insertTag) + Label = tag.Label, + Value = tag.Value, + DataType = tag.GetType().Name, + Data = envelopeService.Serialize(tag) }) .ToArray() ); @@ -76,10 +75,10 @@ ITagQuery tagQuery public static DeleteDocumentsCommand GetDeleteCommand ( - ITransactionCommand transactionCommand, IDeleteTagsCommand deleteTagsCommand + ITransactionCommand transactionCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, deleteTagsCommand.GetTags(transactionCommand.EntityId, transactionCommand.EntityVersionNumber).ToArray()); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, transactionCommand.DeleteTags); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs index 7b9a9ac9..fd1819a2 100644 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs +++ b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs @@ -200,31 +200,31 @@ await CommandDocument .Execute(_sqlDbSession, cancellationToken); - if (transactionCommand.Data is IAddLeasesCommand addLeasesCommand) + if (transactionCommand.AddLeases.Length > 0) { await LeaseDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand, addLeasesCommand) + .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_sqlDbSession, cancellationToken); } - if (transactionCommand.Data is IAddTagsCommand addTagsCommand) + if (transactionCommand.AddTags.Length > 0) { await TagDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand, addTagsCommand) + .GetInsertCommand(_envelopeService, transaction, transactionCommand) .Execute(_sqlDbSession, cancellationToken); } - if (transactionCommand.Data is IDeleteLeasesCommand deleteLeasesCommand) + if (transactionCommand.DeleteLeases.Length > 0) { await LeaseDocument - .GetDeleteCommand(transactionCommand, deleteLeasesCommand) + .GetDeleteCommand(transactionCommand) .Execute(_sqlDbSession, cancellationToken); } - if (transactionCommand.Data is IDeleteTagsCommand deleteTagsCommand) + if (transactionCommand.DeleteTags.Length > 0) { await TagDocument - .GetDeleteCommand(transactionCommand, deleteTagsCommand) + .GetDeleteCommand(transactionCommand) .Execute(_sqlDbSession, cancellationToken); } } diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs index 9a1586cf..670614b3 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs @@ -21,33 +21,33 @@ public TestEntity Reduce(TestEntity entity) } } -public record AddLease(ILease Lease) : DoNothing, IAddLeasesCommand +public record AddLease(ILease Lease) : DoNothing, IAddLeasesCommand { - public IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber) + public IEnumerable GetLeases(TestEntity testEntity) { yield return Lease; } } -public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesCommand +public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesCommand { - public IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber) + public IEnumerable GetLeases(TestEntity testEntity) { yield return Lease; } } -public record AddTag(ITag Tag) : DoNothing, IAddTagsCommand +public record AddTag(ITag Tag) : DoNothing, IAddTagsCommand { - public IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber) + public IEnumerable GetTags(TestEntity testEntity) { yield return Tag; } } -public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsCommand +public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsCommand { - public IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber) + public IEnumerable GetTags(TestEntity testEntity) { yield return Tag; } diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs index 6773db22..70c73d7c 100644 --- a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs @@ -10,7 +10,7 @@ namespace EntityDb.Common.Tests.Implementations.Commands; -public record StoreNumber(ulong Number) : IReducer, IMutator, IAddLeasesCommand, IAddTagsCommand +public record StoreNumber(ulong Number) : IReducer, IMutator, IAddLeasesCommand, IAddTagsCommand { public void Mutate(OneToOneProjection projection) { @@ -25,12 +25,12 @@ public TestEntity Reduce(TestEntity entity) }; } - public IEnumerable GetLeases(Id entityId, VersionNumber entityVersionNumber) + public IEnumerable GetLeases(TestEntity testEntity) { yield return new CountLease(Number); } - public IEnumerable GetTags(Id entityId, VersionNumber entityVersionNumber) + public IEnumerable GetTags(TestEntity testEntity) { yield return new CountTag(Number); } diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs index a649320c..733f807a 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs @@ -1,4 +1,7 @@ -using EntityDb.Abstractions.Transactions; +using System.Collections.Immutable; +using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; @@ -21,7 +24,11 @@ public static IEnumerable CreateFromCommands(Id en { EntityId = entityId, EntityVersionNumber = previousVersionNumber, - Data = CommandSeeder.Create() + Data = CommandSeeder.Create(), + AddLeases = ImmutableArray.Empty, + DeleteLeases = ImmutableArray.Empty, + AddTags = ImmutableArray.Empty, + DeleteTags = ImmutableArray.Empty, }; } } diff --git a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs index df4fa1a5..fa70de5c 100644 --- a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs @@ -97,15 +97,17 @@ private async Task var transaction = transactionBuilder .Append(new AddLease(new Lease(default!, default!, default!))) .Build(default); + + var entity = transactionBuilder.GetEntity(); // ASSERT transaction.Commands.Length.ShouldBe(1); var addLeasesCommand = - transaction.Commands[0].Data.ShouldBeAssignableTo().ShouldNotBeNull(); + transaction.Commands[0].Data.ShouldBeAssignableTo>().ShouldNotBeNull(); - addLeasesCommand.GetLeases(default, default).ShouldNotBeEmpty(); + addLeasesCommand.GetLeases(entity).ShouldNotBeEmpty(); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( From 4ce10efd071b096c6235287f926b4785b12b2708 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 7 Jan 2024 22:50:04 -0800 Subject: [PATCH 61/93] big refactor --- Directory.Build.props | 10 +- EntityDb.sln | 35 - README.md | 79 +- src/.editorconfig | 2 +- .../Annotations/IEntitiesAnnotation.cs | 32 - .../Annotations/IEntityAnnotation.cs | 36 - .../Commands/IAddLeasesCommand.cs | 25 - .../Commands/IAddTagsCommand.cs | 25 - .../Commands/IDeleteLeasesCommand.cs | 26 - .../Commands/IDeleteTagsCommand.cs | 25 - .../Disposables/IDisposableResource.cs | 2 +- .../Entities/Deltas/IAddLeasesDelta.cs | 18 + .../Entities/Deltas/IAddTagsDelta.cs | 18 + .../Entities/Deltas/IDeleteLeasesDelta.cs | 18 + .../Entities/Deltas/IDeleteTagsDelta.cs | 18 + src/EntityDb.Abstractions/Entities/IEntity.cs | 24 + .../Entities/IEntityRepository.cs | 14 +- .../Entities/IEntityRepositoryFactory.cs | 4 +- .../Entities/IEntitySourceBuilder.cs | 56 + .../Entities/IEntitySourceBuilderFactory.cs | 31 + .../ISingleEntitySourceBuilder.cs} | 31 +- .../EntityDb.Abstractions.csproj | 2 +- .../Projections/IProjection.cs | 39 + .../Projections/IProjectionRepository.cs | 3 +- .../IProjectionRepositoryFactory.cs | 5 +- .../IAgentSignatureFilterBuilder.cs | 37 - .../FilterBuilders/ICommandFilterBuilder.cs | 54 - .../FilterBuilders/ILeaseFilterBuilder.cs | 88 - .../FilterBuilders/ITagFilterBuilder.cs | 76 - .../IAgentSignatureSortBuilder.cs | 22 - .../SortBuilders/ICommandSortBuilder.cs | 29 - .../Queries/SortBuilders/ILeaseSortBuilder.cs | 52 - .../Queries/SortBuilders/ITagSortBuilder.cs | 45 - .../Reducers/IReducer.cs | 7 - .../Snapshots/ISnapshot.cs | 22 +- .../Snapshots/ISnapshotRepository.cs | 2 +- .../Transforms}/IMutator.cs | 2 +- .../Transforms}/IReducer.cs | 2 +- .../{ => Sources}/Agents/IAgent.cs | 4 +- .../{ => Sources}/Agents/IAgentAccessor.cs | 4 +- .../Agents/IAgentSignatureAugmenter.cs | 4 +- .../Annotations/IAnnotatedSourceData.cs | 30 + .../Annotations/IAnnotatedSourceGroupData.cs | 30 + .../{Leases => Sources/Attributes}/ILease.cs | 4 +- .../{Tags => Sources/Attributes}/ITag.cs | 2 +- src/EntityDb.Abstractions/Sources/ISource.cs | 30 - .../Sources/ISourceRepository.cs | 162 +- .../Sources/ISourceRepositoryFactory.cs | 18 + .../Sources/ISourceSubscriber.cs | 2 +- src/EntityDb.Abstractions/Sources/Message.cs | 41 + .../Queries/FilterBuilders/IFilterBuilder.cs | 68 +- .../FilterBuilders/ILeaseFilterBuilder.cs | 43 + .../FilterBuilders/IMessageFilterBuilder.cs | 46 + .../IMessageGroupFilterBuilder.cs | 23 + .../FilterBuilders/ITagFilterBuilder.cs | 32 + .../{ => Sources}/Queries/ILeaseQuery.cs | 6 +- .../Queries/IMessageGroupQuery.cs} | 12 +- .../Queries/IMessageQuery.cs} | 22 +- .../{ => Sources}/Queries/IQuery.cs | 5 +- .../{ => Sources}/Queries/ITagQuery.cs | 6 +- .../Queries/SortBuilders/ILeaseSortBuilder.cs | 31 + .../SortBuilders/IMessageGroupSortBuilder.cs | 15 + .../SortBuilders/IMessageSortBuilder.cs | 22 + .../Queries/SortBuilders/ISortBuilder.cs | 20 +- .../Queries/SortBuilders/ITagSortBuilder.cs | 24 + src/EntityDb.Abstractions/Sources/Source.cs | 30 + .../Builders/ITransactionBuilder.cs | 55 - .../Builders/ITransactionBuilderFactory.cs | 30 - .../Transactions/ITransaction.cs | 16 - .../Transactions/ITransactionCommand.cs | 51 - .../Transactions/ITransactionRepository.cs | 148 -- .../ITransactionRepositoryFactory.cs | 18 - src/EntityDb.Abstractions/ValueObjects/Id.cs | 31 +- .../ValueObjects/Pointer.cs | 44 +- .../ValueObjects/Version.cs | 35 + .../ValueObjects/VersionNumber.cs | 51 - .../Annotations/EntitiesAnnotation.cs | 33 - .../Annotations/EntityAnnotation.cs | 36 - .../Documents/IEntitiesDocument.cs | 8 - .../Documents/IEntityDocument.cs | 9 - .../Documents/ITransactionDocument.cs | 14 - .../Entities/EntityRepository.cs | 44 +- .../Entities/EntityRepositoryFactory.cs | 18 +- .../Entities/EntitySourceBuilder.cs | 101 + .../Entities/EntitySourceBuilderFactory.cs | 32 + src/EntityDb.Common/Entities/IEntity.cs | 24 - .../Entities/SingleEntitySourceBuilder.cs | 48 + src/EntityDb.Common/EntityDb.Common.csproj | 3 +- .../CannotWriteInReadOnlyModeException.cs | 6 +- .../Exceptions/DeserializeException.cs | 3 +- .../Exceptions/EntityAlreadyKnownException.cs | 13 - .../EntityBranchAlreadyLoadedException.cs | 12 + .../Exceptions/NoAgentException.cs | 2 +- .../OptimisticConcurrencyException.cs | 31 +- .../Exceptions/SerializeException.cs | 3 +- .../VersionZeroReservedException.cs | 29 - .../Extensions/PointerExtensions.cs | 11 - .../Extensions/ServiceCollectionExtensions.cs | 72 +- .../Extensions/SnapshotExtensions.cs | 13 - .../Extensions/SortBuilderExtensions.cs | 50 +- .../SourceProcessorQueueExtensions.cs | 16 +- .../Extensions/SourceRepositoryExtensions.cs | 89 + .../TransactionRepositoryExtensions.cs | 78 - .../Projections/IProjection.cs | 35 - .../Projections/ProjectionRepository.cs | 23 +- .../ProjectionRepositoryFactory.cs | 19 +- .../Properties/AssemblyInfo.cs | 5 - .../Queries/DeleteLeasesQuery.cs | 37 - .../Queries/GetAgentSignatures.cs | 25 - src/EntityDb.Common/Queries/GetEntityQuery.cs | 37 - .../Queries/GetLastEntityVersionQuery.cs | 23 - .../Queries/GetTransactionQuery.cs | 35 - .../Modified/ModifiedAgentSignatureQuery.cs | 32 - .../Queries/Modified/ModifiedCommandQuery.cs | 31 - .../AgentSignatureReverseSortBuilder.cs | 19 - .../SortBuilders/CommandReverseSortBuilder.cs | 23 - .../Agents/AgentAccessorChain.cs | 8 +- .../Agents/AgentAccessorChainOptions.cs | 4 +- .../{ => Sources}/Agents/StandardAgent.cs | 4 +- .../Agents/UnknownAgentAccessor.cs | 8 +- .../Agents/UnknownAgentSignature.cs | 2 +- .../Annotations/AnnotatedSourceData.cs | 33 + .../Annotations/AnnotatedSourceGroupData.cs | 33 + .../{Leases => Sources/Attributes}/Lease.cs | 4 +- src/EntityDb.Common/Sources/Attributes/Tag.cs | 6 + .../Documents}/DocumentsExtensions.cs | 63 +- .../Sources/Documents/IDocument.cs | 10 + .../Sources/Documents/IMessageDocument.cs | 8 + .../Documents/IMessageGroupDocument.cs | 8 + .../EntitySnapshotSourceProcessor.cs | 38 +- .../Sources/Processors/ISourceProcessor.cs | 4 +- .../ProjectionSnapshotSourceProcessor.cs | 24 +- .../Queues/BufferBlockSourceProcessorQueue.cs | 9 +- .../Queues/ISourceProcessorQueueItem.cs | 6 +- .../Queues/SourceProcessorQueueItem.cs | 2 +- .../Queues/TestModeSourceProcessorQueue.cs | 19 +- .../FilterBuilderExtensions.cs | 10 +- .../Queries/Modified/ModifiedLeaseQuery.cs | 8 +- .../Modified/ModifiedMessageGroupQuery.cs | 32 + .../Queries/Modified/ModifiedMessageQuery.cs | 31 + .../Queries/Modified/ModifiedQueryBase.cs | 4 +- .../Queries/Modified/ModifiedQueryOptions.cs | 10 +- .../Queries/Modified/ModifiedTagQuery.cs | 8 +- .../Queries}/QueryExtensions.cs | 29 +- .../SortBuilders/ReverseLeaseSortBuilder.cs} | 15 +- .../ReverseMessageGroupSortBuilder.cs | 14 + .../SortBuilders/ReverseMessageSortBuilder.cs | 18 + .../SortBuilders/ReverseSortBuilderBase.cs | 9 +- .../SortBuilders/ReverseTagSortBuilder.cs} | 15 +- .../Queries/Standard/DeleteLeasesQuery.cs | 34 + .../Queries/Standard}/DeleteTagsQuery.cs | 13 +- .../Queries/Standard/GetDeltasQuery.cs | 38 + .../Standard/GetLastEntityVersionQuery.cs | 23 + .../Queries/Standard/GetSourceQuery.cs | 35 + .../BufferBlockSourceReprocessorQueue.cs | 76 + .../BufferBlockTransactionReprocessorQueue.cs | 74 - .../ISourceReprocessorQueue.cs | 13 + .../ISourceReprocessorQueueItem.cs | 33 + .../ITransactionReprocessorQueue.cs | 13 - .../ITransactionReprocessorQueueItem.cs | 38 - .../TestModeSourceReprocessorQueue.cs | 59 + .../TestModeTransactionReprocessorQueue.cs | 57 - .../Sources/SourceRepositoryWrapper.cs | 119 ++ .../EntitySnapshotSourceSubscriber.cs | 9 +- .../ProjectionSnapshotSourceSubscriber.cs | 8 +- .../TryCatchSourceRepository.cs} | 24 +- src/EntityDb.Common/Tags/Tag.cs | 6 - .../SingleEntityTransactionBuilder.cs | 49 - .../Builders/TransactionBuilder.cs | 105 - .../Builders/TransactionBuilderFactory.cs | 33 - .../Transactions/Transaction.cs | 13 - .../Transactions/TransactionCommand.cs | 18 - .../TransactionRepositoryWrapper.cs | 117 -- .../DefaultPartialTypeResolver.cs | 22 +- .../Snapshots/InMemorySnapshotRepository.cs | 4 +- ...NumberConverter.cs => VersionConverter.cs} | 12 +- src/EntityDb.Json/EntityDb.Json.csproj | 6 +- .../Envelopes/JsonBytesEnvelopeService.cs | 5 +- .../Envelopes/JsonEnvelopeService.cs | 23 +- .../Envelopes/JsonStringEnvelopeService.cs | 5 +- .../Documents/AgentSignatureDocument.cs | 58 +- .../Documents/CommandDocument.cs | 87 - .../Commands/DeleteDocumentsCommand.cs | 4 +- .../Commands/InsertDocumentsCommand.cs | 4 +- .../Documents/DeltaDocument.cs | 92 + .../Documents/DocumentBase.cs | 32 +- .../Envelopes/MongoDbEnvelopeService.cs} | 10 +- .../Documents/IEntitiesDocument.cs | 8 - .../Documents/IEntityDocument.cs | 8 - .../Documents/ITransactionDocument.cs | 12 - .../Documents/LeaseDocument.cs | 49 +- .../Documents/MessageDocumentBase.cs | 17 + .../Documents/MessageGroupDocumentBase.cs | 18 + .../{ => Documents}/Queries/DocumentQuery.cs | 8 +- .../Queries/DocumentQueryExtensions.cs | 151 ++ .../Serializers/EnvelopeSerializer.cs | 2 +- .../Serializers/IdSerializer.cs | 2 +- .../Serializers/TimeStampSerializer.cs | 2 +- .../Serializers/VersionSerializer.cs} | 20 +- .../Documents/SnapshotDocument.cs | 12 +- src/EntityDb.MongoDb/Documents/TagDocument.cs | 46 +- src/EntityDb.MongoDb/EntityDb.MongoDb.csproj | 10 +- .../Extensions/DocumentQueryExtensions.cs | 162 -- .../Extensions/MongoClientExtensions.cs | 54 +- ...bTransactionRepositoryFactoryExtensions.cs | 27 - .../Extensions/ServiceCollectionExtensions.cs | 18 +- src/EntityDb.MongoDb/Queries/BuilderBase.cs | 11 - .../AgentSignatureFilterBuilder.cs | 21 - .../FilterBuilders/CommandFilterBuilder.cs | 31 - .../FilterBuilders/LeaseFilterBuilder.cs | 45 - .../FilterBuilders/TagFilterBuilder.cs | 40 - .../SortBuilders/AgentSignatureSortBuilder.cs | 20 - .../SortBuilders/CommandSortBuilder.cs | 24 - .../Queries/SortBuilders/LeaseSortBuilder.cs | 39 - .../Queries/SortBuilders/TagSortBuilder.cs | 34 - ...visionMongoDbSnapshotRepositoryFactory.cs} | 10 +- .../Snapshots/MongoDbSnapshotRepository.cs | 5 +- .../MongoDbSnapshotRepositoryFactory.cs | 1 - ...goDbSnapshotRepositoryFactoryExtensions.cs | 8 +- .../Snapshots/Sessions/IMongoSession.cs | 2 +- .../Sessions/MongoDbSnapshotSessionOptions.cs | 10 +- .../Snapshots/Sessions/MongoSession.cs | 37 +- .../Sessions/TestModeMongoSession.cs | 10 +- ...estModeMongoDbSnapshotRepositoryFactory.cs | 5 +- ...rovisionMongoDbSourceRepositoryFactory.cs} | 30 +- .../IMongoDbSourceRepositoryFactory.cs | 24 + .../Sources/MongoDbSourceRepository.cs | 240 +++ .../MongoDbSourceRepositoryFactory.cs} | 28 +- ...ongoDbSourceRepositoryFactoryExtensions.cs | 26 + .../MongoDbSourceRepositoryFactoryWrapper.cs | 41 + .../FilterBuilders/FilterBuilderBase.cs | 33 +- .../FilterBuilders/LeaseFilterBuilder.cs | 25 + .../FilterBuilders/MessageFilterBuilder.cs | 32 + .../MessageGroupFilterBuilder.cs | 20 + .../FilterBuilders/TagFilterBuilder.cs | 20 + .../Queries/MongoDbQueryOptions.cs | 2 +- .../Queries/SortBuilders/LeaseSortBuilder.cs | 24 + .../SortBuilders/MessageGroupSortBuilder.cs | 15 + .../SortBuilders/MessageSortBuilder.cs | 20 + .../Queries/SortBuilders/SortBuilderBase.cs | 20 +- .../Queries/SortBuilders/TagSortBuilder.cs | 19 + .../Sessions/IMongoSession.cs | 6 +- .../Sessions/MongoDbSourceSessionOptions.cs} | 16 +- .../Sessions/MongoSession.cs | 19 +- .../Sessions/TestModeMongoSession.cs | 8 +- ...TestModeMongoDbSourceRepositoryFactory.cs} | 23 +- .../IMongoDbTransactionRepositoryFactory.cs | 24 - .../MongoDbTransactionRepository.cs | 229 --- ...goDbTransactionRepositoryFactoryWrapper.cs | 41 - .../Agents/HttpContextAgentAccessor.cs | 8 +- .../Agents/HttpContextAgentSignature.cs | 66 +- src/EntityDb.Mvc/EntityDb.Mvc.csproj | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../Atlas/Cluster/CreateCollections.cs | 26 +- .../MongoDb/Atlas/Cluster/CreateRole.cs | 95 +- .../MongoDb/Atlas/Cluster/CreateUser.cs | 24 +- .../Serverless/CreateCollectionsServerless.cs | 26 +- ...nd.cs => MongoDbAtlasServerlessCommand.cs} | 0 .../Commands/MongoDb/CommandBase.cs | 13 +- .../Commands/MongoDb/CreateCollections.cs | 18 +- .../Commands/Npgsql/CommandBase.cs | 49 - .../Commands/Npgsql/CreateDatabase.cs | 38 - .../Commands/Npgsql/CreateRole.cs | 53 - .../Commands/Npgsql/CreateTables.cs | 42 - .../Commands/Npgsql/NpgsqlCommand.cs | 17 - .../EntityDb.Provisioner.csproj | 37 +- .../{Resource.cs => MongoDbAtlasResource.cs} | 0 ...oleAction.cs => MongoDbAtlasRoleAction.cs} | 0 .../{UserRole.cs => MongoDbAtlasUserRole.cs} | 0 .../MongoDbAtlas/Models/ServerlessInstance.cs | 3 +- .../MongoDbAtlas/MongoDbAtlasClient.cs | 21 +- src/EntityDb.Provisioner/Program.cs | 2 - ...ool.cs => ConnectionMultiplexerFactory.cs} | 0 src/EntityDb.Redis/EntityDb.Redis.csproj | 6 +- .../Snapshots/RedisSnapshotRepository.cs | 2 +- .../RedisSnapshotRepositoryFactory.cs | 16 +- .../{ => Snapshots}/Sessions/IRedisSession.cs | 2 +- .../{ => Snapshots}/Sessions/RedisSession.cs | 6 +- .../Sessions/RedisSnapshotSessionOptions.cs | 11 +- .../AgentSignature/AgentSignatureDocument.cs | 14 +- .../Documents/Command/CommandDocument.cs | 7 +- src/EntityDb.SqlDb/Documents/DocumentBase.cs | 3 + .../Documents/Lease/LeaseDocument.cs | 11 +- .../Documents/Tag/TagDocument.cs | 9 +- .../Extensions/DocumentQueryExtensions.cs | 10 +- .../AgentSignatureFilterBuilder.cs | 5 + .../SqlDbTransactionRepository.cs | 12 +- .../Extensions/ServiceCollectionExtensions.cs | 6 +- ...nRepository.cs => VoidSourceRepository.cs} | 42 +- .../VoidSourceRepositoryFactory.cs | 16 + .../VoidTransactionRepositoryFactory.cs | 16 - test/Directory.Build.props | 12 +- .../Agents/AgentAccessorTestsBase.cs | 18 +- .../Agents/UnknownAgentAccessorTests.cs | 4 +- .../DatabaseContainerCollection.cs | 2 +- .../DatabaseContainerFixture.cs | 40 +- .../Entities/EntityTests.cs | 139 +- .../EntityDb.Common.Tests.csproj | 28 +- ...ons.cs => FilterBuilderExtensionsTests.cs} | 16 +- .../Implementations/Commands/DoNothing.cs | 54 - .../Implementations/Commands/StoreNumber.cs | 37 - .../DbContexts/GenericDbContext.cs | 60 - .../Implementations/Deltas/AddLease.cs | 13 + .../Implementations/Deltas/AddTag.cs | 13 + .../Implementations/Deltas/DeleteLease.cs | 13 + .../Implementations/Deltas/DeleteTag.cs | 13 + .../Implementations/Deltas/DoNothing.cs | 18 + .../Implementations/Deltas/StoreNumber.cs | 33 + .../Implementations/Entities/TestEntity.cs | 58 +- .../Implementations/Leases/CountLease.cs | 2 +- .../Projections/OneToOneProjection.cs | 117 +- .../Implementations/Queries/CountQuery.cs | 10 +- ...{EntityIdQuery.cs => EntityBranchQuery.cs} | 49 +- .../Queries/EntityVersionNumberQuery.cs | 55 - .../Queries/EntityVersionQuery.cs | 56 + ...TransactionIdQuery.cs => SourceIdQuery.cs} | 48 +- ...eStampQuery.cs => SourceTimeStampQuery.cs} | 41 +- .../{CommandSeeder.cs => DeltaSeeder.cs} | 4 +- .../Implementations/Seeders/LeaseSeeder.cs | 4 +- .../Implementations/Seeders/MessageSeeder.cs | 28 + .../Implementations/Seeders/SourceSeeder.cs | 32 + .../Implementations/Seeders/TagSeeder.cs | 4 +- .../Seeders/TransactionCommandSeeder.cs | 35 - .../Seeders/TransactionSeeder.cs | 31 - .../Snapshots/ISnapshotWithTestLogic.cs | 12 +- .../Implementations/Tags/CountTag.cs | 2 +- .../Projections/ProjectionsTests.cs | 71 +- .../Snapshots/SnapshotTests.cs | 58 +- .../TryCatchSnapshotRepositoryTests.cs | 6 +- .../EntitySnapshotSourceSubscriberTests.cs} | 53 +- .../SingleEntitySourceBuilderTests.cs} | 109 +- .../Sources/SourceTests.cs | 1757 +++++++++++++++++ .../Sources/TryCatchSourceRepositoryTests.cs | 149 ++ test/EntityDb.Common.Tests/StartupBase.cs | 6 +- test/EntityDb.Common.Tests/TestsBase.cs | 309 ++- .../Transactions/TransactionTests.cs | 1743 ---------------- .../TryCatchTransactionRepositoryTests.cs | 140 -- .../TypeResolvers/DefaultTypeResolverTests.cs | 4 +- .../MemberInfoNameTypeResolverTests.cs | 4 +- test/EntityDb.Common.Tests/packages.lock.json | 165 +- .../EntityDb.MongoDb.Tests.csproj | 4 +- .../Envelopes/BsonDocumentEnvelopeTests.cs | 5 +- .../Sessions/MongoSessionTests.cs | 6 +- .../EntityDb.MongoDb.Tests/packages.lock.json | 174 +- .../Agents/HttpContextAgentAccessorTests.cs | 12 +- .../Agents/HttpContextAgentSignatureTests.cs | 64 +- .../EntityDb.Mvc.Tests.csproj | 4 +- test/EntityDb.Mvc.Tests/packages.lock.json | 174 +- .../EntityDb.Redis.Tests.csproj | 4 +- .../Sessions/RedisSessionTests.cs | 4 +- test/EntityDb.Redis.Tests/packages.lock.json | 174 +- 351 files changed, 6444 insertions(+), 7796 deletions(-) delete mode 100644 src/EntityDb.Abstractions/Annotations/IEntitiesAnnotation.cs delete mode 100644 src/EntityDb.Abstractions/Annotations/IEntityAnnotation.cs delete mode 100644 src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs delete mode 100644 src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs delete mode 100644 src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs delete mode 100644 src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs create mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs create mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs create mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs create mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs create mode 100644 src/EntityDb.Abstractions/Entities/IEntity.cs create mode 100644 src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs create mode 100644 src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs rename src/EntityDb.Abstractions/{Transactions/Builders/ISingleEntityTransactionBuilder.cs => Entities/ISingleEntitySourceBuilder.cs} (51%) create mode 100644 src/EntityDb.Abstractions/Projections/IProjection.cs delete mode 100644 src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/FilterBuilders/ICommandFilterBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/FilterBuilders/ILeaseFilterBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/FilterBuilders/ITagFilterBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/SortBuilders/IAgentSignatureSortBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/SortBuilders/ICommandSortBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/SortBuilders/ILeaseSortBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Queries/SortBuilders/ITagSortBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Reducers/IReducer.cs rename src/{EntityDb.Common => EntityDb.Abstractions}/Snapshots/ISnapshot.cs (67%) rename src/EntityDb.Abstractions/{States => Snapshots/Transforms}/IMutator.cs (88%) rename src/EntityDb.Abstractions/{States => Snapshots/Transforms}/IReducer.cs (90%) rename src/EntityDb.Abstractions/{ => Sources}/Agents/IAgent.cs (75%) rename src/EntityDb.Abstractions/{ => Sources}/Agents/IAgentAccessor.cs (71%) rename src/EntityDb.Abstractions/{ => Sources}/Agents/IAgentSignatureAugmenter.cs (73%) create mode 100644 src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs create mode 100644 src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs rename src/EntityDb.Abstractions/{Leases => Sources/Attributes}/ILease.cs (85%) rename src/EntityDb.Abstractions/{Tags => Sources/Attributes}/ITag.cs (84%) delete mode 100644 src/EntityDb.Abstractions/Sources/ISource.cs create mode 100644 src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs create mode 100644 src/EntityDb.Abstractions/Sources/Message.cs rename src/EntityDb.Abstractions/{ => Sources}/Queries/FilterBuilders/IFilterBuilder.cs (52%) create mode 100644 src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs create mode 100644 src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs create mode 100644 src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs create mode 100644 src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs rename src/EntityDb.Abstractions/{ => Sources}/Queries/ILeaseQuery.cs (83%) rename src/EntityDb.Abstractions/{Queries/IAgentSignatureQuery.cs => Sources/Queries/IMessageGroupQuery.cs} (69%) rename src/EntityDb.Abstractions/{Queries/ICommandQuery.cs => Sources/Queries/IMessageQuery.cs} (51%) rename src/EntityDb.Abstractions/{ => Sources}/Queries/IQuery.cs (74%) rename src/EntityDb.Abstractions/{ => Sources}/Queries/ITagQuery.cs (83%) create mode 100644 src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs create mode 100644 src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs create mode 100644 src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs rename src/EntityDb.Abstractions/{ => Sources}/Queries/SortBuilders/ISortBuilder.cs (71%) create mode 100644 src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs create mode 100644 src/EntityDb.Abstractions/Sources/Source.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilderFactory.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/ITransaction.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/ITransactionRepository.cs delete mode 100644 src/EntityDb.Abstractions/Transactions/ITransactionRepositoryFactory.cs create mode 100644 src/EntityDb.Abstractions/ValueObjects/Version.cs delete mode 100644 src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs delete mode 100644 src/EntityDb.Common/Annotations/EntitiesAnnotation.cs delete mode 100644 src/EntityDb.Common/Annotations/EntityAnnotation.cs delete mode 100644 src/EntityDb.Common/Documents/IEntitiesDocument.cs delete mode 100644 src/EntityDb.Common/Documents/IEntityDocument.cs delete mode 100644 src/EntityDb.Common/Documents/ITransactionDocument.cs create mode 100644 src/EntityDb.Common/Entities/EntitySourceBuilder.cs create mode 100644 src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs delete mode 100644 src/EntityDb.Common/Entities/IEntity.cs create mode 100644 src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs delete mode 100644 src/EntityDb.Common/Exceptions/EntityAlreadyKnownException.cs create mode 100644 src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs delete mode 100644 src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs delete mode 100644 src/EntityDb.Common/Extensions/PointerExtensions.cs delete mode 100644 src/EntityDb.Common/Extensions/SnapshotExtensions.cs create mode 100644 src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs delete mode 100644 src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs delete mode 100644 src/EntityDb.Common/Projections/IProjection.cs delete mode 100644 src/EntityDb.Common/Queries/DeleteLeasesQuery.cs delete mode 100644 src/EntityDb.Common/Queries/GetAgentSignatures.cs delete mode 100644 src/EntityDb.Common/Queries/GetEntityQuery.cs delete mode 100644 src/EntityDb.Common/Queries/GetLastEntityVersionQuery.cs delete mode 100644 src/EntityDb.Common/Queries/GetTransactionQuery.cs delete mode 100644 src/EntityDb.Common/Queries/Modified/ModifiedAgentSignatureQuery.cs delete mode 100644 src/EntityDb.Common/Queries/Modified/ModifiedCommandQuery.cs delete mode 100644 src/EntityDb.Common/Queries/SortBuilders/AgentSignatureReverseSortBuilder.cs delete mode 100644 src/EntityDb.Common/Queries/SortBuilders/CommandReverseSortBuilder.cs rename src/EntityDb.Common/{ => Sources}/Agents/AgentAccessorChain.cs (87%) rename src/EntityDb.Common/{ => Sources}/Agents/AgentAccessorChainOptions.cs (91%) rename src/EntityDb.Common/{ => Sources}/Agents/StandardAgent.cs (64%) rename src/EntityDb.Common/{ => Sources}/Agents/UnknownAgentAccessor.cs (75%) rename src/EntityDb.Common/{ => Sources}/Agents/UnknownAgentSignature.cs (76%) create mode 100644 src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs create mode 100644 src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs rename src/EntityDb.Common/{Leases => Sources/Attributes}/Lease.cs (52%) create mode 100644 src/EntityDb.Common/Sources/Attributes/Tag.cs rename src/EntityDb.Common/{Extensions => Sources/Documents}/DocumentsExtensions.cs (54%) create mode 100644 src/EntityDb.Common/Sources/Documents/IDocument.cs create mode 100644 src/EntityDb.Common/Sources/Documents/IMessageDocument.cs create mode 100644 src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs rename src/EntityDb.Common/{Extensions => Sources/Queries/FilterBuilders}/FilterBuilderExtensions.cs (91%) rename src/EntityDb.Common/{ => Sources}/Queries/Modified/ModifiedLeaseQuery.cs (74%) create mode 100644 src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs rename src/EntityDb.Common/{ => Sources}/Queries/Modified/ModifiedQueryBase.cs (72%) rename src/EntityDb.Common/{ => Sources}/Queries/Modified/ModifiedQueryOptions.cs (83%) rename src/EntityDb.Common/{ => Sources}/Queries/Modified/ModifiedTagQuery.cs (74%) rename src/EntityDb.Common/{Extensions => Sources/Queries}/QueryExtensions.cs (62%) rename src/EntityDb.Common/{Queries/SortBuilders/LeaseReverseSortBuilder.cs => Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs} (58%) create mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs create mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs rename src/EntityDb.Common/{ => Sources}/Queries/SortBuilders/ReverseSortBuilderBase.cs (62%) rename src/EntityDb.Common/{Queries/SortBuilders/TagReverseSortBuilder.cs => Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs} (53%) create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs rename src/EntityDb.Common/{Queries => Sources/Queries/Standard}/DeleteTagsQuery.cs (61%) create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs delete mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs delete mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs delete mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs create mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs delete mode 100644 src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs create mode 100644 src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs rename src/EntityDb.Common/{Transactions/TryCatchTransactionRepository.cs => Sources/TryCatchSourceRepository.cs} (68%) delete mode 100644 src/EntityDb.Common/Tags/Tag.cs delete mode 100644 src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs delete mode 100644 src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs delete mode 100644 src/EntityDb.Common/Transactions/Builders/TransactionBuilderFactory.cs delete mode 100644 src/EntityDb.Common/Transactions/Transaction.cs delete mode 100644 src/EntityDb.Common/Transactions/TransactionCommand.cs delete mode 100644 src/EntityDb.Common/Transactions/TransactionRepositoryWrapper.cs rename src/EntityDb.Json/Converters/{VersionNumberConverter.cs => VersionConverter.cs} (67%) delete mode 100644 src/EntityDb.MongoDb/Documents/CommandDocument.cs rename src/EntityDb.MongoDb/{ => Documents}/Commands/DeleteDocumentsCommand.cs (77%) rename src/EntityDb.MongoDb/{ => Documents}/Commands/InsertDocumentsCommand.cs (74%) create mode 100644 src/EntityDb.MongoDb/Documents/DeltaDocument.cs rename src/EntityDb.MongoDb/{Envelopes/BsonDocumentEnvelopeService.cs => Documents/Envelopes/MongoDbEnvelopeService.cs} (84%) delete mode 100644 src/EntityDb.MongoDb/Documents/IEntitiesDocument.cs delete mode 100644 src/EntityDb.MongoDb/Documents/IEntityDocument.cs delete mode 100644 src/EntityDb.MongoDb/Documents/ITransactionDocument.cs create mode 100644 src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs create mode 100644 src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs rename src/EntityDb.MongoDb/{ => Documents}/Queries/DocumentQuery.cs (63%) create mode 100644 src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs rename src/EntityDb.MongoDb/{ => Documents}/Serializers/EnvelopeSerializer.cs (95%) rename src/EntityDb.MongoDb/{ => Documents}/Serializers/IdSerializer.cs (91%) rename src/EntityDb.MongoDb/{ => Documents}/Serializers/TimeStampSerializer.cs (93%) rename src/EntityDb.MongoDb/{Serializers/VersionNumberSerializer.cs => Documents/Serializers/VersionSerializer.cs} (50%) delete mode 100644 src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs delete mode 100644 src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs delete mode 100644 src/EntityDb.MongoDb/Queries/BuilderBase.cs delete mode 100644 src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/FilterBuilders/CommandFilterBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/FilterBuilders/LeaseFilterBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/FilterBuilders/TagFilterBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/SortBuilders/CommandSortBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/SortBuilders/LeaseSortBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Queries/SortBuilders/TagSortBuilder.cs rename src/EntityDb.MongoDb/Snapshots/{AutoProvisionMongoDbTransactionRepositoryFactory.cs => AutoProvisionMongoDbSnapshotRepositoryFactory.cs} (92%) rename src/EntityDb.MongoDb/{Extensions => Snapshots}/MongoDbSnapshotRepositoryFactoryExtensions.cs (82%) rename src/EntityDb.MongoDb/{Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs => Sources/AutoProvisionMongoDbSourceRepositoryFactory.cs} (58%) create mode 100644 src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs create mode 100644 src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs rename src/EntityDb.MongoDb/{Transactions/MongoDbTransactionRepositoryFactory.cs => Sources/MongoDbSourceRepositoryFactory.cs} (53%) create mode 100644 src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryExtensions.cs create mode 100644 src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryWrapper.cs rename src/EntityDb.MongoDb/{ => Sources}/Queries/FilterBuilders/FilterBuilderBase.cs (69%) create mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs rename src/EntityDb.MongoDb/{ => Sources}/Queries/MongoDbQueryOptions.cs (86%) create mode 100644 src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs rename src/EntityDb.MongoDb/{ => Sources}/Queries/SortBuilders/SortBuilderBase.cs (58%) create mode 100644 src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs rename src/EntityDb.MongoDb/{Transactions => Sources}/Sessions/IMongoSession.cs (82%) rename src/EntityDb.MongoDb/{Transactions/Sessions/MongoDbTransactionSessionOptions.cs => Sources/Sessions/MongoDbSourceSessionOptions.cs} (77%) rename src/EntityDb.MongoDb/{Transactions => Sources}/Sessions/MongoSession.cs (92%) rename src/EntityDb.MongoDb/{Transactions => Sources}/Sessions/TestModeMongoSession.cs (84%) rename src/EntityDb.MongoDb/{Transactions/TestModeMongoDbTransactionRepositoryFactory.cs => Sources/TestModeMongoDbSourceRepositoryFactory.cs} (54%) delete mode 100644 src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs delete mode 100644 src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs delete mode 100644 src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs rename src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/{MongoDbServerlessCommand.cs => MongoDbAtlasServerlessCommand.cs} (100%) delete mode 100644 src/EntityDb.Provisioner/Commands/Npgsql/CommandBase.cs delete mode 100644 src/EntityDb.Provisioner/Commands/Npgsql/CreateDatabase.cs delete mode 100644 src/EntityDb.Provisioner/Commands/Npgsql/CreateRole.cs delete mode 100644 src/EntityDb.Provisioner/Commands/Npgsql/CreateTables.cs delete mode 100644 src/EntityDb.Provisioner/Commands/Npgsql/NpgsqlCommand.cs rename src/EntityDb.Provisioner/MongoDbAtlas/Models/{Resource.cs => MongoDbAtlasResource.cs} (100%) rename src/EntityDb.Provisioner/MongoDbAtlas/Models/{RoleAction.cs => MongoDbAtlasRoleAction.cs} (100%) rename src/EntityDb.Provisioner/MongoDbAtlas/Models/{UserRole.cs => MongoDbAtlasUserRole.cs} (100%) rename src/EntityDb.Redis/ConnectionMultiplexers/{ConnectionMultiplexerPool.cs => ConnectionMultiplexerFactory.cs} (100%) rename src/EntityDb.Redis/{ => Snapshots}/Sessions/IRedisSession.cs (85%) rename src/EntityDb.Redis/{ => Snapshots}/Sessions/RedisSession.cs (92%) rename src/EntityDb.Redis/{ => Snapshots}/Sessions/RedisSnapshotSessionOptions.cs (78%) rename src/EntityDb.Void/Transactions/{VoidTransactionRepository.cs => VoidSourceRepository.cs} (54%) create mode 100644 src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs delete mode 100644 src/EntityDb.Void/Transactions/VoidTransactionRepositoryFactory.cs rename test/EntityDb.Common.Tests/Extensions/{FilterBuilderExtensions.cs => FilterBuilderExtensionsTests.cs} (91%) delete mode 100644 test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs rename test/EntityDb.Common.Tests/Implementations/Queries/{EntityIdQuery.cs => EntityBranchQuery.cs} (57%) delete mode 100644 test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionNumberQuery.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs rename test/EntityDb.Common.Tests/Implementations/Queries/{TransactionIdQuery.cs => SourceIdQuery.cs} (53%) rename test/EntityDb.Common.Tests/Implementations/Queries/{TransactionTimeStampQuery.cs => SourceTimeStampQuery.cs} (69%) rename test/EntityDb.Common.Tests/Implementations/Seeders/{CommandSeeder.cs => DeltaSeeder.cs} (61%) create mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs rename test/EntityDb.Common.Tests/{Transactions/EntitySnapshotTransactionSubscriberTests.cs => Sources/EntitySnapshotSourceSubscriberTests.cs} (62%) rename test/EntityDb.Common.Tests/{Transactions/SingleEntityTransactionBuilderTests.cs => Sources/SingleEntitySourceBuilderTests.cs} (63%) create mode 100644 test/EntityDb.Common.Tests/Sources/SourceTests.cs create mode 100644 test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs delete mode 100644 test/EntityDb.Common.Tests/Transactions/TransactionTests.cs delete mode 100644 test/EntityDb.Common.Tests/Transactions/TryCatchTransactionRepositoryTests.cs diff --git a/Directory.Build.props b/Directory.Build.props index 0eef874e..b9d8bfa6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,10 +2,10 @@ net7.0 enable - enable - + enable + - - - + + + \ No newline at end of file diff --git a/EntityDb.sln b/EntityDb.sln index 3f73be88..6434dc1f 100644 --- a/EntityDb.sln +++ b/EntityDb.sln @@ -18,8 +18,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Mvc", "src\EntityD EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Redis", "src\EntityDb.Redis\EntityDb.Redis.csproj", "{F7BB95AE-A032-4170-89E3-E8A1D492EFB5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Void", "src\EntityDb.Void\EntityDb.Void.csproj", "{C4239113-18BE-4418-87A8-E9617EF0A2DF}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{92484C44-2754-4C1D-BD46-98D83E4020EE}" ProjectSection(SolutionItems) = preProject test\Directory.Build.props = test\Directory.Build.props @@ -39,18 +37,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution global.json = global.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.InMemory", "src\EntityDb.InMemory\EntityDb.InMemory.csproj", "{31C5BEDB-9B04-4FE4-9AF5-AE682C0E7643}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Npgsql", "src\EntityDb.Npgsql\EntityDb.Npgsql.csproj", "{2AADF21D-4F26-4BD6-852A-B28208863FDD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.SqlDb", "src\EntityDb.SqlDb\EntityDb.SqlDb.csproj", "{F2491666-31D1-47B5-A493-F25E167D1FDF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Json", "src\EntityDb.Json\EntityDb.Json.csproj", "{4936FFE0-98E5-43A2-89C9-0415A13CAA9B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Provisioner", "src\EntityDb.Provisioner\EntityDb.Provisioner.csproj", "{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.EntityFramework", "src\EntityDb.EntityFramework\EntityDb.EntityFramework.csproj", "{199606BF-6283-4684-A224-4DA7E80D8F45}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,10 +67,6 @@ Global {F7BB95AE-A032-4170-89E3-E8A1D492EFB5}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7BB95AE-A032-4170-89E3-E8A1D492EFB5}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7BB95AE-A032-4170-89E3-E8A1D492EFB5}.Release|Any CPU.Build.0 = Release|Any CPU - {C4239113-18BE-4418-87A8-E9617EF0A2DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C4239113-18BE-4418-87A8-E9617EF0A2DF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C4239113-18BE-4418-87A8-E9617EF0A2DF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C4239113-18BE-4418-87A8-E9617EF0A2DF}.Release|Any CPU.Build.0 = Release|Any CPU {CF316519-525E-4A67-BF12-1FDDF802B878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CF316519-525E-4A67-BF12-1FDDF802B878}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF316519-525E-4A67-BF12-1FDDF802B878}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -97,18 +83,6 @@ Global {FA2AD2E9-84DA-4667-BF46-140B0B050563}.Debug|Any CPU.Build.0 = Debug|Any CPU {FA2AD2E9-84DA-4667-BF46-140B0B050563}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA2AD2E9-84DA-4667-BF46-140B0B050563}.Release|Any CPU.Build.0 = Release|Any CPU - {31C5BEDB-9B04-4FE4-9AF5-AE682C0E7643}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {31C5BEDB-9B04-4FE4-9AF5-AE682C0E7643}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31C5BEDB-9B04-4FE4-9AF5-AE682C0E7643}.Release|Any CPU.ActiveCfg = Release|Any CPU - {31C5BEDB-9B04-4FE4-9AF5-AE682C0E7643}.Release|Any CPU.Build.0 = Release|Any CPU - {2AADF21D-4F26-4BD6-852A-B28208863FDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2AADF21D-4F26-4BD6-852A-B28208863FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2AADF21D-4F26-4BD6-852A-B28208863FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2AADF21D-4F26-4BD6-852A-B28208863FDD}.Release|Any CPU.Build.0 = Release|Any CPU - {F2491666-31D1-47B5-A493-F25E167D1FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F2491666-31D1-47B5-A493-F25E167D1FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2491666-31D1-47B5-A493-F25E167D1FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F2491666-31D1-47B5-A493-F25E167D1FDF}.Release|Any CPU.Build.0 = Release|Any CPU {4936FFE0-98E5-43A2-89C9-0415A13CAA9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4936FFE0-98E5-43A2-89C9-0415A13CAA9B}.Debug|Any CPU.Build.0 = Debug|Any CPU {4936FFE0-98E5-43A2-89C9-0415A13CAA9B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -117,10 +91,6 @@ Global {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.Build.0 = Release|Any CPU - {199606BF-6283-4684-A224-4DA7E80D8F45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {199606BF-6283-4684-A224-4DA7E80D8F45}.Debug|Any CPU.Build.0 = Debug|Any CPU - {199606BF-6283-4684-A224-4DA7E80D8F45}.Release|Any CPU.ActiveCfg = Release|Any CPU - {199606BF-6283-4684-A224-4DA7E80D8F45}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -131,17 +101,12 @@ Global {0BEA3379-6637-44F2-8332-7EA81B02BBBF} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {8A485A1A-54E4-48AD-9B35-97D286F432CE} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {F7BB95AE-A032-4170-89E3-E8A1D492EFB5} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} - {C4239113-18BE-4418-87A8-E9617EF0A2DF} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {CF316519-525E-4A67-BF12-1FDDF802B878} = {92484C44-2754-4C1D-BD46-98D83E4020EE} {8D9C0976-BF78-439A-BBCB-3CFC2EEF1A0F} = {92484C44-2754-4C1D-BD46-98D83E4020EE} {B8B6E5A5-5154-4629-9A38-9F0E65575F30} = {92484C44-2754-4C1D-BD46-98D83E4020EE} {FA2AD2E9-84DA-4667-BF46-140B0B050563} = {92484C44-2754-4C1D-BD46-98D83E4020EE} - {31C5BEDB-9B04-4FE4-9AF5-AE682C0E7643} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} - {2AADF21D-4F26-4BD6-852A-B28208863FDD} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} - {F2491666-31D1-47B5-A493-F25E167D1FDF} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {4936FFE0-98E5-43A2-89C9-0415A13CAA9B} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} - {199606BF-6283-4684-A224-4DA7E80D8F45} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E9D288EE-9351-4018-ABE8-B0968AEB0465} diff --git a/README.md b/README.md index 1f84db91..3f2bd44d 100644 --- a/README.md +++ b/README.md @@ -25,38 +25,39 @@ information on the statement. (If I'm wrong, you should consider getting a new b ## How does EntityDb.NET implement Event Sourcing? -There are several core objects at the heart of this implementation. Encalsulating these objects are various repositories. - -1. Transaction Repository - - Agents - - Commands - - Tags - - Leases +There are several core objects at the heart of this implementation. Encapsulating these objects are various +repositories. + +1. Source Repository + - Agents + - Deltas + - Tags + - Leases + - Aliases 2. Snapshot Repository - - Snapshots -2. Entity Repository - - Transaction Repository - - Optional: Snapshot Repository -3. Projection Repository - - Transaction Repository - - Optional: Snapshot Repository + - Snapshots +3. Entity Repository + - Transaction Repository + - Optional: Snapshot Repository +4. Projection Repository + - Transaction Repository + - Optional: Snapshot Repository ### Transactions -A transaction represents an atomic operation on multiple entities. A transaction is committed atomically or not -at all. If some step in the transaction fails, the entire transaction should fail. +A source represents an atomic operation on multiple entities. A source is committed atomically or not +at all. If some step in the source fails, the entire source should fail. ### Agents -An agent is an actor that can execute transactions. For example, if a transaction is initiated via an HTTP API, you -might use the `HttpContextAgent` - it's signature includes headers and connection information, and it uses the -ClaimsPrincipal to decide if an agent has a particular role required for authorized commands. +An agent is an actor that can record sources. For example, if a source is initiated via an HTTP API, you +might use the `HttpContextAgent` - it's signature includes headers and connection information. -### Commands +### Delta -A command represents the intent to perform some operation on a single entity. Going back to the bank account example, -one command could be `PerformDeposit` while another could be `PerformWithdrawl`. The things that you can do are -commands. +A delta represents a change to a single entity. Going back to the bank account example, +one delta could be `PerformDeposit` while another could be `PerformWithdrawl`. The things that you can do (commands), +as well as things that are done elsewhere (events), are delta. ### Tags @@ -69,23 +70,29 @@ and `Value` is `Savings`. The number of savings accounts in the system would be A lease is like a tag, except that it has a uniqueness constraint. Many banks have online portals, allowing bank members to see their accounts on the internet. From the bank's perspective, all of the accounts should be tied to a member id, -probably a guid. But the member will not want to remember nor lookup this guid - they will want to use a username. What -you can do in EntityDb is make a lease for member entities where the entity id is the member id, the `Label` -is `Username` -and the `Value` is whatever username the member wants to use. If an attempt to commit a transaction is made that would +probably a guid. But the member will not want to remember nor lookup this id - they will want to use a username. What +you can do in EntityDb is make a lease for members where the `Scope` is `Global`, the `Label` +is `Username`, and the `Value` is whatever username the member wants to use. If an attempt to commit a source is made +that would violate the uniqueness constraint, it will be rejected. (This is obnoxious behavior for the user, though, so the bank should check before attempting to commit to see if the username is available and give immediate feedback to choose a different username). +### Aliases + +An alias is like a lease, except that it doesn't have a scope or a label. It is +unique per entity branch name and a value, and it can be used to achieve idempotency automatically. +Source repositories will skip messages if its alias is already recorded. + ### Snapshots -A snapshot is a stateful object at a given point in time. They always have an identifier and a version number. -Together, the identifier and version number called a pointer. You can request different versions of a given snapshot +A snapshot is a stateful object at a given point in time. They always have an identifier and a version. +Together, the identifier and version called a pointer. You can request different versions of a given snapshot by using different pointers! -In the context of snapshots, the reserved version number is reserved for pointing to the latest snapshot. -So if you want the latest version, you use a pointer with the exact id and the reserved version number. -If you want a specific version, you can create pointer with the exact id and version number you want. +In the context of snapshots, the reserved version is reserved for pointing to the latest snapshot. +So if you want the latest version, you use a pointer with the exact id and the reserved version. +If you want a specific version, you can create pointer with the exact id and version you want. The balance on your bank account is a snapshot. You can build that snapshot by summing all of the deposits and withdrawls on your account. If you look at the bank statements, you will most likely see the snapshot of each bank @@ -93,7 +100,7 @@ account for that statement, along with all of the deposits, withdrawls, and inte ### Entities -An entity is conceptually an aggregate root inside of a bounded context, and it extends the concept of a snapshot. +An entity is conceptually an aggregate id inside of a bounded context, and it extends the concept of a snapshot. In the banking example, there are multiple entities. You have a membership at the bank. That's an entity. You probably have a checking account. That's an entity. And you might even have a savings account. That is also an entity! @@ -101,10 +108,6 @@ Which bounded contexts these entiies live in is up to the business. ### Projections -A projection is an aggregate, but notably _not_ the aggregate root, and it too extends the concept of a snapshot. +A projection is an aggregate, but notably _not_ the aggregate id, and it too extends the concept of a snapshot. In the banking example, one example of a projection could be your entire account balance. It can be anything, though! You are not constrained in what data you want to use for your projection. - -### Communications - -- Coming Soon!TM diff --git a/src/.editorconfig b/src/.editorconfig index c342ff50..db62372b 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,5 +1,5 @@ # Remove the line below if you want to inherit .editorconfig settings from higher directories -root = true +id = true # C# files [*.cs] diff --git a/src/EntityDb.Abstractions/Annotations/IEntitiesAnnotation.cs b/src/EntityDb.Abstractions/Annotations/IEntitiesAnnotation.cs deleted file mode 100644 index c9cc6193..00000000 --- a/src/EntityDb.Abstractions/Annotations/IEntitiesAnnotation.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Annotations; - -/// -/// Represents data for multiple entities that have already been committed, along with relevant information not -/// contained -/// in the data. -/// -/// The type of data. -public interface IEntitiesAnnotation -{ - /// - /// The transaction id associated with the data. - /// - Id TransactionId { get; } - - /// - /// The transaction timestamp associated with the data. - /// - TimeStamp TransactionTimeStamp { get; } - - /// - /// The entity ids associated with the data. - /// - Id[] EntityIds { get; } - - /// - /// The data. - /// - TData Data { get; } -} diff --git a/src/EntityDb.Abstractions/Annotations/IEntityAnnotation.cs b/src/EntityDb.Abstractions/Annotations/IEntityAnnotation.cs deleted file mode 100644 index 9051138a..00000000 --- a/src/EntityDb.Abstractions/Annotations/IEntityAnnotation.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Annotations; - -/// -/// Represents data for a single entity that has already been committed, along with relevant information not contained -/// in the data. -/// -/// The type of data. -public interface IEntityAnnotation -{ - /// - /// The transaction id associated with the data. - /// - Id TransactionId { get; } - - /// - /// The transaction timestamp associated with the data. - /// - TimeStamp TransactionTimeStamp { get; } - - /// - /// The entity id associated with the data. - /// - Id EntityId { get; } - - /// - /// The entity version number associated with the data. - /// - VersionNumber EntityVersionNumber { get; } - - /// - /// The data. - /// - TData Data { get; } -} diff --git a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs deleted file mode 100644 index 7e12a7d6..00000000 --- a/src/EntityDb.Abstractions/Commands/IAddLeasesCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Leases; - -namespace EntityDb.Abstractions.Commands; - -/// -/// Represents a command that adds leases. -/// -/// The type of the entity. -public interface IAddLeasesCommand -{ - /// - /// Returns the leases that need to be added. - /// - /// The entity for which leases will be added. - /// The leases that need to be added. - IEnumerable GetLeases(TEntity entity); -} - -/// -[Obsolete("Please use IAddLeasesCommand instead. This will be removed in a future version.", true)] -public interface IAddLeasesCommand -{ - /// - IEnumerable GetLeases() => throw new NotImplementedException(); -} diff --git a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs deleted file mode 100644 index 923777e0..00000000 --- a/src/EntityDb.Abstractions/Commands/IAddTagsCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Tags; - -namespace EntityDb.Abstractions.Commands; - -/// -/// Represents a command that adds tags. -/// -/// The type of the entity -public interface IAddTagsCommand -{ - /// - /// Returns the tags that need to be added. - /// - /// The entity for which tags will be added - /// The tags that need to be added. - IEnumerable GetTags(TEntity entity); -} - -/// -[Obsolete("Please use IAddTagsCommand instead. This will be removed in a future version.", true)] -public interface IAddTagsCommand -{ - /// - IEnumerable GetTags() => throw new NotImplementedException(); -} diff --git a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs deleted file mode 100644 index 9c1e3cc1..00000000 --- a/src/EntityDb.Abstractions/Commands/IDeleteLeasesCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Commands; - -/// -/// Represents a command that deletes leases. -/// -/// The type of the entity -public interface IDeleteLeasesCommand -{ - /// - /// Returns the leases that need to be deleted. - /// - /// The entity for which leases will be removed. - /// The leases that need to be deleted. - IEnumerable GetLeases(TEntity entity); -} - -/// -[Obsolete("Please use IDeleteLeasesCommand instead. This will be removed in a future version.", true)] -public interface IDeleteLeasesCommand -{ - /// - IEnumerable GetLeases() => throw new NotImplementedException(); -} diff --git a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs b/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs deleted file mode 100644 index dd4fc47f..00000000 --- a/src/EntityDb.Abstractions/Commands/IDeleteTagsCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Tags; - -namespace EntityDb.Abstractions.Commands; - -/// -/// Represents a command that deletes tags. -/// -/// The type of the entity. -public interface IDeleteTagsCommand -{ - /// - /// Returns the tags that need to be deleted. - /// - /// The entity for which tags will be deleted. - /// The tags that need to be deleted. - IEnumerable GetTags(TEntity entity); -} - -/// -[Obsolete("Please use IDeleteTagsCommand instead. This will be removed in a future version,", true)] -public interface IDeleteTagsCommand -{ - /// - IEnumerable GetTags() => throw new NotImplementedException(); -} diff --git a/src/EntityDb.Abstractions/Disposables/IDisposableResource.cs b/src/EntityDb.Abstractions/Disposables/IDisposableResource.cs index d5862de8..dcd9aa9a 100644 --- a/src/EntityDb.Abstractions/Disposables/IDisposableResource.cs +++ b/src/EntityDb.Abstractions/Disposables/IDisposableResource.cs @@ -1,7 +1,7 @@ namespace EntityDb.Abstractions.Disposables; /// -/// Marks a resource as disposable and provides a default implementation. +/// Marks a resource as disposable (sync and async) /// public interface IDisposableResource : IDisposable, IAsyncDisposable { diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs new file mode 100644 index 00000000..b85614e0 --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Entities.Deltas; + +/// +/// Represents a delta that adds leases. +/// +/// The type of the entity. +public interface IAddLeasesDelta + where TEntity : IEntity +{ + /// + /// Returns the leases that need to be added. + /// + /// The entity for which leases will be added. + /// The leases that need to be added. + IEnumerable GetLeases(TEntity entity); +} diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs new file mode 100644 index 00000000..b57c32b6 --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Entities.Deltas; + +/// +/// Represents a delta that adds tags. +/// +/// The type of the entity +public interface IAddTagsDelta + where TEntity : IEntity +{ + /// + /// Returns the tags that need to be added. + /// + /// The entity for which tags will be added + /// The tags that need to be added. + IEnumerable GetTags(TEntity entity); +} diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs new file mode 100644 index 00000000..4e144943 --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Entities.Deltas; + +/// +/// Represents a delta that deletes leases. +/// +/// The type of the entity +public interface IDeleteLeasesDelta + where TEntity : IEntity +{ + /// + /// Returns the leases that need to be deleted. + /// + /// The entity for which leases will be removed. + /// The leases that need to be deleted. + IEnumerable GetLeases(TEntity entity); +} diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs new file mode 100644 index 00000000..5b91c15d --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Entities.Deltas; + +/// +/// Represents a delta that deletes tags. +/// +/// The type of the entity. +public interface IDeleteTagsDelta + where TEntity : IEntity +{ + /// + /// Returns the tags that need to be deleted. + /// + /// The root for which tags will be deleted. + /// The tags that need to be deleted. + IEnumerable GetTags(TEntity entity); +} diff --git a/src/EntityDb.Abstractions/Entities/IEntity.cs b/src/EntityDb.Abstractions/Entities/IEntity.cs new file mode 100644 index 00000000..d2a7e0bb --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/IEntity.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Snapshots; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Indicates the entity is compatible with several EntityDb.Common implementations. +/// +/// The type of the entity. +public interface IEntity : ISnapshot +{ + /// + /// Returns true if is not expected to throw an exception. + /// + /// The delta + /// true if is not expected to throw an exception. + static abstract bool CanReduce(object delta); + + /// + /// Returns a new that incorporates the deltas. + /// + /// The delta + /// A new that incorporates . + TEntity Reduce(object delta); +} diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IEntityRepository.cs index 19dd0fae..90d99d73 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepository.cs @@ -1,20 +1,20 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Entities; /// -/// Encapsulates the transaction repository and the snapshot repository of an entity. +/// Encapsulates the source repository and the snapshot repository of an entity. /// /// The type of the entity. public interface IEntityRepository : IDisposableResource { /// - /// The backing transaction repository. + /// The backing source repository. /// - ITransactionRepository TransactionRepository { get; } + ISourceRepository SourceRepository { get; } /// /// The backing snapshot repository (if snapshot is available). @@ -30,10 +30,10 @@ public interface IEntityRepository : IDisposableResource Task GetSnapshot(Pointer entityPointer, CancellationToken cancellationToken = default); /// - /// Inserts a single transaction with an atomic commit. + /// Atomically commits a source. /// - /// The transaction. + /// The source. /// A cancellation token. /// true if the insert succeeded, or false if the insert failed. - Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default); + Task Commit(Source source, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs index 975863b1..06a29466 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs @@ -9,10 +9,10 @@ public interface IEntityRepositoryFactory /// /// Create a new instance of /// - /// The agent's use case for the transaction repository. + /// The agent's use case for the source repository. /// The agent's use case for the snapshot repository. /// A cancellation token. /// A new instance of . - Task> CreateRepository(string transactionSessionOptionsName, + Task> CreateRepository(string sourceSessionOptionsName, string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs new file mode 100644 index 00000000..63ecf5fa --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs @@ -0,0 +1,56 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Provides a way to construct an . Note that no operations are permanent +/// until you call and pass the result to a source repository. +/// +/// The type of the entity in the source. +public interface IEntitySourceBuilder +{ + /// + /// Returns a associated with a given branch, if it is known. + /// + /// The id associated with the entity. + /// A associated with , if it is known. + TEntity GetEntity(Id entityId); + + /// + /// Indicates whether or not a associated with a given branch is in memory. + /// + /// The id of the entity. + /// + /// true if a associated with is in memory, or + /// else false. + /// + bool IsEntityKnown(Id entityId); + + /// + /// Associate a with a given entity branch. + /// + /// A branch associated with a . + /// A . + /// The source builder. + /// + /// Call this method to load an entity that already exists before calling + /// . + /// + IEntitySourceBuilder Load(Id entityId, TEntity entity); + + /// + /// Adds a single delta to the source with a given entity branch. + /// + /// The branch associated with the . + /// The new delta that modifies the . + /// The source builder. + IEntitySourceBuilder Append(Id entityId, object delta); + + /// + /// Returns a new instance of . + /// + /// A new id for the new source. + /// A new instance of . + Source Build(Id sourceId); +} diff --git a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs new file mode 100644 index 00000000..98d55cd6 --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs @@ -0,0 +1,31 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Represents a type used to create instances of or +/// . +/// +/// +public interface IEntitySourceBuilderFactory +{ + /// + /// Creates a new instance of . + /// + /// The name of the agent signature options. + /// A cancellation token. + /// A new instance of . + Task> Create(string agentSignatureOptionsName, + CancellationToken cancellationToken = default); + + /// + /// Creates a new instance of . + /// + /// The name of the agent signature options. + /// The id of the entity. + /// A cancellation token. + /// A new instance of . + Task> CreateForSingleEntity(string agentSignatureOptionsName, + Id entityId, + CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs b/src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs similarity index 51% rename from src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs rename to src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs index 7086df99..9c5e00c2 100644 --- a/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs +++ b/src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs @@ -1,15 +1,16 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Abstractions.Transactions.Builders; +namespace EntityDb.Abstractions.Entities; /// -/// A transaction builder for a single entity. +/// A source builder for a single entity. /// /// The type of the entity. -public interface ISingleEntityTransactionBuilder +public interface ISingleEntitySourceBuilder { /// - /// The id used for all transaction builder methods, where applicable. + /// The id used for all source builder methods, where applicable. /// Id EntityId { get; } @@ -29,24 +30,24 @@ public interface ISingleEntityTransactionBuilder /// Associate a . /// /// A - /// The transaction builder. + /// The source builder. /// /// Call this method to load an entity that already exists before calling /// . /// - ISingleEntityTransactionBuilder Load(TEntity entity); + ISingleEntitySourceBuilder Load(TEntity entity); /// - /// Adds a single command to the transaction. + /// Adds a single delta to the source. /// - /// The new command that modifies the . - /// The transaction builder. - ISingleEntityTransactionBuilder Append(object command); + /// The new delta that modifies the . + /// The source builder. + ISingleEntitySourceBuilder Append(object delta); /// - /// Returns a new instance of . + /// Returns a new instance of . /// - /// A new id for the new transaction. - /// A new instance of . - ITransaction Build(Id transactionId); + /// A new id for the new source. + /// A new instance of . + Source Build(Id sourceId); } diff --git a/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj b/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj index 9e371f45..c1b3c486 100644 --- a/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj +++ b/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj @@ -1,9 +1,9 @@  - EntityDb EventSourcing DDD CQRS An abstraction layer for the EntityDb suite of packages. + diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs new file mode 100644 index 00000000..2f56420c --- /dev/null +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -0,0 +1,39 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Projections; + +/// +/// Provides basic functionality for the common implementation of projections. +/// +/// +public interface IProjection : ISnapshot +{ + /// + /// Incorporates the source into the projection. + /// + /// The source of information + void Mutate(Source source); + + /// + /// Returns a that finds sources that need to be passed to the reducer. + /// + /// A service provider for fetching repositories. + /// A pointer to the desired projection state + /// A cancellation token + /// + /// A that is used to load the rest of the source messages for the given projection + /// pointer. + /// + IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Pointer projectionPointer, + CancellationToken cancellationToken); + + /// + /// Maps a source to a set of entity ids. May be empty if the source does not map to the projection. + /// + /// A source + /// The entity branches for the projections. + static abstract IEnumerable EnumerateEntityIds(Source source); +} diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index c9681b9e..2e809559 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Projections; @@ -9,7 +8,7 @@ namespace EntityDb.Abstractions.Projections; /// Encapsulates the snapshot repository for a projection. /// /// The type of the projection. -public interface IProjectionRepository : ISourceRepository, IDisposableResource +public interface IProjectionRepository : IDisposableResource { /// /// The backing snapshot repository. diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs index a12c6502..2e90e03a 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs @@ -9,10 +9,9 @@ public interface IProjectionRepositoryFactory /// /// Create a new instance of /// - /// The agent's use case for the transaction repository. /// The agent's use case for the snapshot repository. /// A cancellation token. /// A new instance of . - Task> CreateRepository(string transactionSessionOptionsName, - string snapshotSessionOptionsName, CancellationToken cancellationToken = default); + Task> CreateRepository(string snapshotSessionOptionsName, + CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs deleted file mode 100644 index 718f6199..00000000 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs +++ /dev/null @@ -1,37 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Queries.FilterBuilders; - -/// -/// Builds a for a agentSignature query. -/// -/// The type of filter used by the repository. -public interface IAgentSignatureFilterBuilder : IFilterBuilder -{ - /// - [Obsolete("Please use SubjectIdsIn instead. This will be removed in a future version.")] - TFilter EntityIdsIn(params Id[] entityIds) => SubjectIdsIn(entityIds); - - /// - /// Returns a that only includes agentSignatures with any source id which is contained - /// in a set of source ids. - /// - /// The set of subject ids. - /// - /// A that only includes agentSignatures with any source id which is contained in - /// . - /// - TFilter SubjectIdsIn(params Id[] subjectIds); - - /// - /// Returns a that only includes agentSignatures whose type is contained in a set of - /// agentSignature - /// types. - /// - /// The agentSignature types. - /// - /// A that only includes agentSignatures whose type is contained in - /// . - /// - TFilter AgentSignatureTypeIn(params Type[] agentSignatureTypes); -} diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/ICommandFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/ICommandFilterBuilder.cs deleted file mode 100644 index 774787e8..00000000 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/ICommandFilterBuilder.cs +++ /dev/null @@ -1,54 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Queries.FilterBuilders; - -/// -/// Builds a for a command query. -/// -/// The type of filter used by the repository. -public interface ICommandFilterBuilder : IFilterBuilder -{ - /// - /// Returns a that only includes commands with an entity id which is contained in a set - /// of entity ids. - /// - /// The set of entity ids. - /// - /// A that only includes commands with an entity id which is contained in - /// . - /// - TFilter EntityIdIn(params Id[] entityIds); - - /// - /// Returns a that only includes commands with an entity version number greater than or - /// equal to an entity version number. - /// - /// The entity version number. - /// - /// A that only includes commands with an entity version number greater than or - /// equal to . - /// - TFilter EntityVersionNumberGte(VersionNumber entityVersionNumber); - - /// - /// Returns a that only includes commands with an entity version number less than or - /// equal to an entity version number. - /// - /// The entity version number. - /// - /// A that only includes commands with an entity version number less than or equal - /// to . - /// - TFilter EntityVersionNumberLte(VersionNumber entityVersionNumber); - - /// - /// Returns a that only includes commands whose type is contained in a set of command - /// types. - /// - /// The command types. - /// - /// A that only includes commands whose type is contained in - /// . - /// - TFilter CommandTypeIn(params Type[] commandTypes); -} diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/ILeaseFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/ILeaseFilterBuilder.cs deleted file mode 100644 index a2a9c558..00000000 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/ILeaseFilterBuilder.cs +++ /dev/null @@ -1,88 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Queries.FilterBuilders; - -/// -/// Builds a for a lease query. -/// -/// The type of filter used by the repository. -public interface ILeaseFilterBuilder : IFilterBuilder -{ - /// - /// Returns a that only includes leases with an entity id which is contained in a set - /// of entity ids. - /// - /// The set of entity ids. - /// - /// A that only includes leases with an entity id which is contained in - /// . - /// - TFilter EntityIdIn(params Id[] entityIds); - - /// - /// Returns a that only includes leases with an entity version number greater than or - /// equal to an entity version number. - /// - /// The entity version number. - /// - /// A that only includes leases with an entity version number greater than or - /// equal to . - /// - TFilter EntityVersionNumberGte(VersionNumber entityVersionNumber); - - /// - /// Returns a that only includes leases with an entity version number less than or - /// equal to an entity version number. - /// - /// The entity version number. - /// - /// A that only includes leases with an entity version number less than or equal - /// to . - /// - TFilter EntityVersionNumberLte(VersionNumber entityVersionNumber); - - /// - /// Returns a that only includes leases whose type is contained in a set of lease - /// types. - /// - /// The lease types. - /// - /// A that only includes leases whose type is contained in - /// . - /// - TFilter LeaseTypeIn(params Type[] leaseTypes); - - /// - /// Returns a that only includes leases whose is - /// a particular value. - /// - /// The lease scope - /// - /// A that only includes leases whose is - /// . - /// - TFilter LeaseScopeEq(string scope); - - /// - /// Returns a that only includes leases whose is - /// a particular value. - /// - /// The lease label - /// - /// A that only includes leases whose is - /// . - /// - TFilter LeaseLabelEq(string label); - - /// - /// Returns a that only includes leases whose is - /// a particular value. - /// - /// The lease value - /// - /// A that only includes leases whose is - /// . - /// - TFilter LeaseValueEq(string value); -} diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/ITagFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/ITagFilterBuilder.cs deleted file mode 100644 index 52069d1a..00000000 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/ITagFilterBuilder.cs +++ /dev/null @@ -1,76 +0,0 @@ -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Queries.FilterBuilders; - -/// -/// Builds a for a tag query. -/// -/// The type of filter used by the repository. -public interface ITagFilterBuilder : IFilterBuilder -{ - /// - /// Returns a that only includes tags with an entity id which is contained in a set of - /// entity ids. - /// - /// The set of entity ids. - /// - /// A that only includes tags with an entity id which is contained in - /// . - /// - TFilter EntityIdIn(params Id[] entityIds); - - /// - /// Returns a that only includes tags with an entity version number greater than or - /// equal to an entity version number. - /// - /// The entity version number. - /// - /// A that only includes tags with an entity version number greater than or equal - /// to . - /// - TFilter EntityVersionNumberGte(VersionNumber entityVersionNumber); - - /// - /// Returns a that only includes tags with an entity version number less than or equal - /// to an entity version number. - /// - /// The entity version number. - /// - /// A that only includes tags with an entity version number less than or equal to - /// . - /// - TFilter EntityVersionNumberLte(VersionNumber entityVersionNumber); - - /// - /// Returns a that only includes tags whose type is contained in a set of tag types. - /// - /// The tag types. - /// - /// A that only includes tags whose type is contained in - /// . - /// - TFilter TagTypeIn(params Type[] tagTypes); - - /// - /// Returns a that only includes tags whose is - /// a particular value. - /// - /// The tag labels - /// - /// A that only includes tags whose is - /// . - /// - TFilter TagLabelEq(string label); - - /// - /// Returns a that only includes tags whose is - /// a particular value. - /// - /// The tag values - /// - /// A that only includes tags whose is - /// . - /// - TFilter TagValueEq(string value); -} diff --git a/src/EntityDb.Abstractions/Queries/SortBuilders/IAgentSignatureSortBuilder.cs b/src/EntityDb.Abstractions/Queries/SortBuilders/IAgentSignatureSortBuilder.cs deleted file mode 100644 index 18f699f9..00000000 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/IAgentSignatureSortBuilder.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace EntityDb.Abstractions.Queries.SortBuilders; - -/// -/// Builds a for a agentSignature query. -/// -/// The type of sort used by the repository. -public interface IAgentSignatureSortBuilder : ISortBuilder -{ - /// - /// Returns a that orders agentSignatures by entity ids. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders agentSignatures by entity ids. - TSort EntityIds(bool ascending); - - /// - /// Returns a that orders agentSignatures by type. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders agentSignatures by type. - TSort AgentSignatureType(bool ascending); -} diff --git a/src/EntityDb.Abstractions/Queries/SortBuilders/ICommandSortBuilder.cs b/src/EntityDb.Abstractions/Queries/SortBuilders/ICommandSortBuilder.cs deleted file mode 100644 index 4585a80f..00000000 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/ICommandSortBuilder.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace EntityDb.Abstractions.Queries.SortBuilders; - -/// -/// Builds a for a command query. -/// -/// The type of sort used by the repository. -public interface ICommandSortBuilder : ISortBuilder -{ - /// - /// Returns a that orders commands by entity id. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders commands by entity id. - TSort EntityId(bool ascending); - - /// - /// Returns a that orders commands by entity version number. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders commands by entity version number. - TSort EntityVersionNumber(bool ascending); - - /// - /// Returns a that orders commands by type. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders commands by type. - TSort CommandType(bool ascending); -} diff --git a/src/EntityDb.Abstractions/Queries/SortBuilders/ILeaseSortBuilder.cs b/src/EntityDb.Abstractions/Queries/SortBuilders/ILeaseSortBuilder.cs deleted file mode 100644 index 473a8e28..00000000 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/ILeaseSortBuilder.cs +++ /dev/null @@ -1,52 +0,0 @@ -using EntityDb.Abstractions.Leases; - -namespace EntityDb.Abstractions.Queries.SortBuilders; - -/// -/// Builds a sort for a lease query. -/// -/// The type of sort used by the repository. -public interface ILeaseSortBuilder : ISortBuilder -{ - /// - /// Returns a that orders leases by entity id. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders leases by entity id. - TSort EntityId(bool ascending); - - /// - /// Returns a that orders leases by entity version number. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders leases by entity version number. - TSort EntityVersionNumber(bool ascending); - - /// - /// Returns a that orders leases by type. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders leases by type. - TSort LeaseType(bool ascending); - - /// - /// Returns a that orders leases by . - /// - /// Pass true for ascending order or false for descending order. - /// A that orders leases by . - TSort LeaseScope(bool ascending); - - /// - /// Returns a that orders leases by . - /// - /// Pass true for ascending order or false for descending order. - /// A that orders leases by . - TSort LeaseLabel(bool ascending); - - /// - /// Returns a that orders leases by . - /// - /// Pass true for ascending order or false for descending order. - /// A that orders leases by . - TSort LeaseValue(bool ascending); -} diff --git a/src/EntityDb.Abstractions/Queries/SortBuilders/ITagSortBuilder.cs b/src/EntityDb.Abstractions/Queries/SortBuilders/ITagSortBuilder.cs deleted file mode 100644 index e6394571..00000000 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/ITagSortBuilder.cs +++ /dev/null @@ -1,45 +0,0 @@ -using EntityDb.Abstractions.Tags; - -namespace EntityDb.Abstractions.Queries.SortBuilders; - -/// -/// Builds a sort for a tag query. -/// -/// The type of sort used by the repository. -public interface ITagSortBuilder : ISortBuilder -{ - /// - /// Returns a that orders tags by entity id. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders tags by entity id. - TSort EntityId(bool ascending); - - /// - /// Returns a that orders tags by entity version number. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders tags by entity version number. - TSort EntityVersionNumber(bool ascending); - - /// - /// Returns a that orders tags by type. - /// - /// Pass true for ascending order or false for descending order. - /// A that orders tags by type. - TSort TagType(bool ascending); - - /// - /// Returns a that orders tags by . - /// - /// Pass true for ascending order or false for descending order. - /// A that orders tags by . - TSort TagLabel(bool ascending); - - /// - /// Returns a that orders tags by . - /// - /// Pass true for ascending order or false for descending order. - /// A that orders tags by . - TSort TagValue(bool ascending); -} diff --git a/src/EntityDb.Abstractions/Reducers/IReducer.cs b/src/EntityDb.Abstractions/Reducers/IReducer.cs deleted file mode 100644 index 9f838e1c..00000000 --- a/src/EntityDb.Abstractions/Reducers/IReducer.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace EntityDb.Abstractions.Reducers; - -/// -[Obsolete("Please use the IReducer in the States namespace. This one will be removed in a future version.")] -public interface IReducer : States.IReducer -{ -} diff --git a/src/EntityDb.Common/Snapshots/ISnapshot.cs b/src/EntityDb.Abstractions/Snapshots/ISnapshot.cs similarity index 67% rename from src/EntityDb.Common/Snapshots/ISnapshot.cs rename to src/EntityDb.Abstractions/Snapshots/ISnapshot.cs index 11fd26a3..e76d0e2e 100644 --- a/src/EntityDb.Common/Snapshots/ISnapshot.cs +++ b/src/EntityDb.Abstractions/Snapshots/ISnapshot.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Common.Snapshots; +namespace EntityDb.Abstractions.Snapshots; /// /// Indicates that the snapshot is compatible with several EntityDb.Common implementations. @@ -11,29 +11,23 @@ public interface ISnapshot /// /// Creates a new instance of a . /// - /// The id of the snapshot. + /// The pointer of the snapshot. /// A new instance of . - static abstract TSnapshot Construct(Id snapshotId); + static abstract TSnapshot Construct(Pointer pointer); /// - /// Returns the id of this snapshot. + /// Returns a pointer for the current state of the snapshot. /// - /// The id of this snapshot. - Id GetId(); - - /// - /// Returns the version number of this snapshot. - /// - /// The version number of this snapshot. - VersionNumber GetVersionNumber(); + /// A pointer for the current state of the snapshot + Pointer GetPointer(); /// /// Indicates if this snapshot instance version should be recorded (independent of the latest snapshot). /// /// true if this snapshot instance should be recorded, or else false. /// - /// You would use this if you intent to fetch a snapshot at multiple version numbers and don't want to hit - /// the transaction database when it can be avoided. + /// You would use this if you intent to fetch a snapshot at multiple versions and don't want to hit + /// the source database when it can be avoided. /// bool ShouldRecord(); diff --git a/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs b/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs index 31d4d68a..f039d562 100644 --- a/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs +++ b/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs @@ -24,7 +24,7 @@ public interface ISnapshotRepository : IDisposableResource /// /// Inserts a snapshot. /// - /// A pointer to a specific snapshot. + /// A pointer to a snapshot. /// The snapshot. /// A cancellation token. /// true if the insert succeeded, or false if the insert failed. diff --git a/src/EntityDb.Abstractions/States/IMutator.cs b/src/EntityDb.Abstractions/Snapshots/Transforms/IMutator.cs similarity index 88% rename from src/EntityDb.Abstractions/States/IMutator.cs rename to src/EntityDb.Abstractions/Snapshots/Transforms/IMutator.cs index 60eb7ab7..148fba22 100644 --- a/src/EntityDb.Abstractions/States/IMutator.cs +++ b/src/EntityDb.Abstractions/Snapshots/Transforms/IMutator.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.States; +namespace EntityDb.Abstractions.Snapshots.Transforms; /// /// Represents a type that can mutate one state into another state. diff --git a/src/EntityDb.Abstractions/States/IReducer.cs b/src/EntityDb.Abstractions/Snapshots/Transforms/IReducer.cs similarity index 90% rename from src/EntityDb.Abstractions/States/IReducer.cs rename to src/EntityDb.Abstractions/Snapshots/Transforms/IReducer.cs index 818e733b..3edb7eb9 100644 --- a/src/EntityDb.Abstractions/States/IReducer.cs +++ b/src/EntityDb.Abstractions/Snapshots/Transforms/IReducer.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.States; +namespace EntityDb.Abstractions.Snapshots.Transforms; /// /// Represents a type that can reduce one state into another state. diff --git a/src/EntityDb.Abstractions/Agents/IAgent.cs b/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs similarity index 75% rename from src/EntityDb.Abstractions/Agents/IAgent.cs rename to src/EntityDb.Abstractions/Sources/Agents/IAgent.cs index aa3a2dec..40992be1 100644 --- a/src/EntityDb.Abstractions/Agents/IAgent.cs +++ b/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs @@ -1,9 +1,9 @@ using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Abstractions.Agents; +namespace EntityDb.Abstractions.Sources.Agents; /// -/// Represents an actor who can interact with transactions. +/// Represents an actor who can record sources. /// public interface IAgent { diff --git a/src/EntityDb.Abstractions/Agents/IAgentAccessor.cs b/src/EntityDb.Abstractions/Sources/Agents/IAgentAccessor.cs similarity index 71% rename from src/EntityDb.Abstractions/Agents/IAgentAccessor.cs rename to src/EntityDb.Abstractions/Sources/Agents/IAgentAccessor.cs index a0bf7fbf..fa4d6553 100644 --- a/src/EntityDb.Abstractions/Agents/IAgentAccessor.cs +++ b/src/EntityDb.Abstractions/Sources/Agents/IAgentAccessor.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.Agents; +namespace EntityDb.Abstractions.Sources.Agents; /// /// Represents a type that can access an instance of . @@ -11,5 +11,5 @@ public interface IAgentAccessor /// The name of the signature options object. /// A cancellation token. /// The agent. - Task GetAgentAsync(string signatureOptionsName, CancellationToken cancellationToken = default); + Task GetAgent(string signatureOptionsName, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Agents/IAgentSignatureAugmenter.cs b/src/EntityDb.Abstractions/Sources/Agents/IAgentSignatureAugmenter.cs similarity index 73% rename from src/EntityDb.Abstractions/Agents/IAgentSignatureAugmenter.cs rename to src/EntityDb.Abstractions/Sources/Agents/IAgentSignatureAugmenter.cs index 3c3ff651..5bc114f2 100644 --- a/src/EntityDb.Abstractions/Agents/IAgentSignatureAugmenter.cs +++ b/src/EntityDb.Abstractions/Sources/Agents/IAgentSignatureAugmenter.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.Agents; +namespace EntityDb.Abstractions.Sources.Agents; /// /// Represents a type that can augment an agent signature by @@ -11,5 +11,5 @@ public interface IAgentSignatureAugmenter /// /// A cancellation token. /// A dictionary of application-specific information. - Task> GetApplicationInfoAsync(CancellationToken cancellationToken = default); + Task> GetApplicationInfo(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs new file mode 100644 index 00000000..a7bb3de6 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs @@ -0,0 +1,30 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Sources.Annotations; + +/// +/// Annotated source data +/// +/// The type of the data +public interface IAnnotatedSourceData +{ + /// + /// The id of the source + /// + Id SourceId { get; } + + /// + /// The time stamp of the source + /// + TimeStamp SourceTimeStamp { get; } + + /// + /// The data + /// + TData Data { get; } + + /// + /// A pointer to the entity + /// + Pointer EntityPointer { get; } +} diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs new file mode 100644 index 00000000..bd839a40 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs @@ -0,0 +1,30 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Sources.Annotations; + +/// +/// Annotated source group data +/// +/// The type of the data +public interface IAnnotatedSourceGroupData +{ + /// + /// The id of the source + /// + Id SourceId { get; } + + /// + /// The time stamp of the source + /// + TimeStamp SourceTimeStamp { get; } + + /// + /// The data + /// + TData Data { get; } + + /// + /// The pointers to the entities + /// + Pointer[] EntityPointers { get; } +} diff --git a/src/EntityDb.Abstractions/Leases/ILease.cs b/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs similarity index 85% rename from src/EntityDb.Abstractions/Leases/ILease.cs rename to src/EntityDb.Abstractions/Sources/Attributes/ILease.cs index ae71ac5a..ac71c772 100644 --- a/src/EntityDb.Abstractions/Leases/ILease.cs +++ b/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs @@ -1,10 +1,10 @@ -namespace EntityDb.Abstractions.Leases; +namespace EntityDb.Abstractions.Sources.Attributes; /// /// Represents a single metadata property and the context in which the metadata property must be unique. /// /// -/// The transaction repository is responsible for enforcing the uniqueness constraint. +/// The source repository is responsible for enforcing the uniqueness constraint. /// If a lease needs to be unique in a global context, a constant should be used as the for all /// instances of the lease. /// If a lease does not need to be unique in a global context, the entity id (or some other id which is unique to the diff --git a/src/EntityDb.Abstractions/Tags/ITag.cs b/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs similarity index 84% rename from src/EntityDb.Abstractions/Tags/ITag.cs rename to src/EntityDb.Abstractions/Sources/Attributes/ITag.cs index 4638f865..35a4faf7 100644 --- a/src/EntityDb.Abstractions/Tags/ITag.cs +++ b/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.Tags; +namespace EntityDb.Abstractions.Sources.Attributes; /// /// Represents a single metadata property, which can be used to query the current state of an entity. diff --git a/src/EntityDb.Abstractions/Sources/ISource.cs b/src/EntityDb.Abstractions/Sources/ISource.cs deleted file mode 100644 index 99d9de18..00000000 --- a/src/EntityDb.Abstractions/Sources/ISource.cs +++ /dev/null @@ -1,30 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Sources; - -/// -/// Represents a generic piece of information. -/// -/// -/// For now, only implements -/// this, but once events are added, ICommunication -/// will also implement this. -/// -public interface ISource -{ - /// - /// The id associated with the source. - /// - Id Id { get; } - - /// - /// The date and time associated with the source. - /// - TimeStamp TimeStamp { get; } - - /// - /// The signature of the source agent. - /// - object AgentSignature { get; } -} diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs index bfc53d51..8089311b 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -1,14 +1,148 @@ -using EntityDb.Abstractions.Transactions; - -namespace EntityDb.Abstractions.Sources; - -/// -/// Represents sources of information. -/// -public interface ISourceRepository -{ - /// - /// The backing transaction repository. - /// - ITransactionRepository TransactionRepository { get; } -} +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Sources; + +/// +/// Represents an explicit set of objects which represent a complete history of a set of entities. +/// +public interface ISourceRepository : IDisposableResource +{ + /// + /// Returns the source ids which are found by a message group query. + /// + /// The message group query. + /// A cancellation token. + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the source ids which are found by a message query. + /// + /// The message query. + /// A cancellation token. + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(IMessageQuery messageQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the source ids which are found by a lease query. + /// + /// The lease query. + /// A cancellation token. + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the source ids which are found by a tag query. + /// + /// The tag query. + /// A cancellation token. + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the entity pointers which are found by a agentSignature query. + /// + /// The message group query. + /// A cancellation token. + /// The entity pointers which are found by . + IAsyncEnumerable EnumerateEntityPointers(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the entity pointers which are found by a message query. + /// + /// The message query. + /// A cancellation token. + /// The entity pointers which are found by . + IAsyncEnumerable EnumerateEntityPointers(IMessageQuery messageQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the entity pointers which are found by a lease query. + /// + /// The lease query. + /// A cancellation token. + /// The entity pointers which are found by . + IAsyncEnumerable EnumerateEntityPointers(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the entity pointers which are found by a tag query. + /// + /// The tag query. + /// A cancellation token. + /// The entity pointers which are found by . + IAsyncEnumerable EnumerateEntityPointers(ITagQuery tagQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the agentSignatures which are found by an message group query. + /// + /// The message group query. + /// A cancellation token. + /// The agent signatures which are found by . + IAsyncEnumerable EnumerateAgentSignatures(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the deltas which are found by a message query. + /// + /// The message query. + /// A cancellation token. + /// The deltas which are found by . + IAsyncEnumerable EnumerateDeltas(IMessageQuery messageQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the leases which are found by a lease query. + /// + /// The lease query. + /// A cancellation token. + /// The leases which are found by . + IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the tags which are found by a tag query. + /// + /// The tag query. + /// A cancellation token. + /// The tags which are found by . + IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the annotated agent signatures which are found by a message group query. + /// + /// The message group query. + /// A cancellation token. + /// The annotated agent signatures which are found by . + IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default); + + /// + /// Returns the annotated deltas which are found by a message query. + /// + /// The message query. + /// A cancellation token. + /// The annotated deltas which are found by . + IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageQuery messageQuery, + CancellationToken cancellationToken = default); + + /// + /// Atomically commits a single source. + /// + /// The source. + /// A cancellation token. + /// true if the insert succeeded, or false if the insert failed. + Task Commit(Source source, CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs b/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs new file mode 100644 index 00000000..c2669ff3 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Disposables; + +namespace EntityDb.Abstractions.Sources; + +/// +/// Represents a type used to create instances of . +/// +public interface ISourceRepositoryFactory : IDisposableResource +{ + /// + /// Creates a new instance of . + /// + /// The agent's use case for the repository. + /// A cancellation token. + /// A new instance of . + Task CreateRepository(string sourceSessionOptionsName, + CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs b/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs index 673bc35a..d1092ac5 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs @@ -9,5 +9,5 @@ public interface ISourceSubscriber /// Called when a source has been committed. /// /// The committed source. - void Notify(ISource source); + void Notify(Source source); } diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs new file mode 100644 index 00000000..affecae5 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -0,0 +1,41 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.ValueObjects; +using System.Collections.Immutable; + +namespace EntityDb.Abstractions.Sources; + +/// +/// A message that belongs to a single source +/// +public sealed record Message +{ + /// + /// A pointer to the entity + /// + public required Pointer EntityPointer { get; init; } + + /// + /// The data. + /// + public required object Delta { get; init; } + + /// + /// The leases to be added. + /// + public ImmutableArray AddLeases { get; init; } = ImmutableArray.Empty; + + /// + /// The tags to be added. + /// + public ImmutableArray AddTags { get; init; } = ImmutableArray.Empty; + + /// + /// The tags to be deleted. + /// + public ImmutableArray DeleteLeases { get; init; } = ImmutableArray.Empty; + + /// + /// The aliases to be added. + /// + public ImmutableArray DeleteTags { get; init; } = ImmutableArray.Empty; +} diff --git a/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs similarity index 52% rename from src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs index 9cebb2fc..f9190f59 100644 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs @@ -1,68 +1,71 @@ using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Abstractions.Queries.FilterBuilders; +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// -/// Builds a for an object repository. Possible objects include: agentSignatures, -/// commands, facts, tags, and events. +/// Builds a for an object. Possible objects include: +/// agent signatures, deltas, facts, tags, and aliases /// /// The type of filter used by the repository. public interface IFilterBuilder { - /// - [Obsolete("Please use SourceTimeStampGte instead. This will be removed in a future version.")] - TFilter TransactionTimeStampGte(TimeStamp transactionTimeStamp) => SourceTimeStampGte(transactionTimeStamp); - - /// - [Obsolete("Please use SourceTimeStampLte instead. This will be removed in a future version.")] - TFilter TransactionTimeStampLte(TimeStamp transactionTimeStamp) => SourceTimeStampLte(transactionTimeStamp); - - /// - [Obsolete("Please use SourceIdIn instead. This will be removed in a future version.")] - TFilter TransactionIdIn(params Id[] transactionIds) => SourceIdIn(transactionIds); - /// - /// Returns a that only includes objects with a source timestamp greater than or - /// equal to a source timestamp. + /// Returns a that only includes objects with a source + /// timestamp greater than or equal to a source timestamp. /// /// The source timestamp. /// - /// A that only includes objects with an source timestamp greater than or - /// equal to . + /// A that only includes objects with an source timestamp + /// greater than or equal to . /// TFilter SourceTimeStampGte(TimeStamp sourceTimeStamp); /// - /// Returns a that only includes objects with a source timestamp less than or - /// equal to a source timestamp. + /// Returns a that only includes objects with a source + /// timestamp less than or equal to a source timestamp. /// /// The source timestamp. /// - /// A that only includes objects with an source timestamp less than or equal - /// to . + /// A that only includes objects with an source timestamp + /// less than or equal to . /// TFilter SourceTimeStampLte(TimeStamp sourceTimeStamp); /// - /// Returns a that only includes objects with an source id which is contained in a - /// set of source ids. + /// Returns a that only includes objects with an source + /// id which is contained in a set of source ids. /// /// The set of source ids. /// - /// A that only includes objects with an source id which is contained in - /// . + /// A that only includes objects with an source id which + /// is contained in . /// TFilter SourceIdIn(params Id[] sourceIds); + /// + /// Returns a that only includes objects whose data type + /// is contained in a set of data types. + /// + /// The data types. + /// + /// A that only includes objects whose data type is contained + /// in . + /// + TFilter DataTypeIn(params Type[] dataTypes); + /// /// Returns a that excludes objects which do match a filter. /// /// The filter. - /// A that excludes objects which do match . + /// + /// A that excludes objects which do match + /// . + /// TFilter Not(TFilter filter); /// - /// Returns a that excludes objects which do not match all filters in a set of filters. + /// Returns a that excludes objects which do not match + /// all filters in a set of filters. /// /// The set of filters. /// @@ -72,12 +75,13 @@ public interface IFilterBuilder TFilter And(params TFilter[] filters); /// - /// Returns a that excludes objects which do not match any filter in a set of filters. + /// Returns a that excludes objects which do not match + /// any filter in a set of filters. /// /// The set of filters. /// - /// A that excludes objects which do not match any filter in - /// . + /// A that excludes objects which do not match any filter + /// in . /// TFilter Or(params TFilter[] filters); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs new file mode 100644 index 00000000..cfc25b06 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs @@ -0,0 +1,43 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; + +/// +/// Builds a for a lease query. +/// +/// The type of filter used by the repository. +public interface ILeaseFilterBuilder : IMessageFilterBuilder +{ + /// + /// Returns a that only includes leases whose is + /// a particular value. + /// + /// The lease scope + /// + /// A that only includes leases whose is + /// . + /// + TFilter LeaseScopeEq(string scope); + + /// + /// Returns a that only includes leases whose is + /// a particular value. + /// + /// The lease label + /// + /// A that only includes leases whose is + /// . + /// + TFilter LeaseLabelEq(string label); + + /// + /// Returns a that only includes leases whose is + /// a particular value. + /// + /// The lease value + /// + /// A that only includes leases whose is + /// . + /// + TFilter LeaseValueEq(string value); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs new file mode 100644 index 00000000..94290054 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs @@ -0,0 +1,46 @@ +using EntityDb.Abstractions.ValueObjects; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; + +/// +/// Builds a for an object associated +/// with a single message. Possible objects include: +/// deltas, facts, tags, and aliases +/// +/// The type of filter used by the repository. +public interface IMessageFilterBuilder : IFilterBuilder +{ + /// + /// Returns a that only includes objects with an entity + /// branch in a set of entity branches. + /// + /// The entity branches. + /// + /// Returns a that only includes objects with an entity + /// branch in . + /// + TFilter EntityIdIn(params Id[] entityIds); + + /// + /// Returns a that only includes objects with an entity + /// version greater than or equal to a specific entity version. + /// + /// The entity branches. + /// + /// Returns a that only includes objects with an entity + /// version greater than or equal to . + /// + TFilter EntityVersionGte(Version entityVersion); + + /// + /// Returns a that only includes objects with an entity + /// version greater than or equal to a specific entity version. + /// + /// The entity branches. + /// + /// Returns a that only includes objects with an entity + /// version less than or equal to . + /// + TFilter EntityVersionLte(Version entityVersion); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs new file mode 100644 index 00000000..dc1e2e40 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs @@ -0,0 +1,23 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; + +/// +/// Builds a for an object associated +/// with one or more messages. Possible objects include: +/// agent signatures +/// +/// The type of filter used by the repository. +public interface IMessageGroupFilterBuilder : IFilterBuilder +{ + /// + /// Returns a that only includes objects with any entity + /// branch in a set of entity branches. + /// + /// The entity branches. + /// + /// Returns a that only includes objects with any entity + /// branch in . + /// + TFilter AnyEntityIdIn(params Id[] entityIds); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs new file mode 100644 index 00000000..d05ea254 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; + +/// +/// Builds a for a tag query. +/// +/// The type of filter used by the repository. +public interface ITagFilterBuilder : IMessageFilterBuilder +{ + /// + /// Returns a that only includes tags whose is + /// a particular value. + /// + /// The tag labels + /// + /// A that only includes tags whose is + /// . + /// + TFilter TagLabelEq(string label); + + /// + /// Returns a that only includes tags whose is + /// a particular value. + /// + /// The tag values + /// + /// A that only includes tags whose is + /// . + /// + TFilter TagValueEq(string value); +} diff --git a/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs similarity index 83% rename from src/EntityDb.Abstractions/Queries/ILeaseQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs index c6ce4c7e..211de809 100644 --- a/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Abstractions.Queries; +namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on leases. diff --git a/src/EntityDb.Abstractions/Queries/IAgentSignatureQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageGroupQuery.cs similarity index 69% rename from src/EntityDb.Abstractions/Queries/IAgentSignatureQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IMessageGroupQuery.cs index 1478abc4..4e677774 100644 --- a/src/EntityDb.Abstractions/Queries/IAgentSignatureQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageGroupQuery.cs @@ -1,12 +1,12 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Abstractions.Queries; +namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on agentSignatures. /// -public interface IAgentSignatureQuery : IQuery +public interface IMessageGroupQuery : IQuery { /// /// Returns a built from a agentSignature filter builder. @@ -14,7 +14,7 @@ public interface IAgentSignatureQuery : IQuery /// The type of filter used by the repository. /// The agentSignature filter builder. /// A built from . - TFilter GetFilter(IAgentSignatureFilterBuilder builder); + TFilter GetFilter(IMessageGroupFilterBuilder builder); /// /// Returns a built from a agentSignature sort builder. @@ -22,5 +22,5 @@ public interface IAgentSignatureQuery : IQuery /// The type of sort used by the repository. /// The agentSignature sort builder. /// A built from . - TSort? GetSort(IAgentSignatureSortBuilder builder); + TSort? GetSort(IMessageGroupSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Queries/ICommandQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs similarity index 51% rename from src/EntityDb.Abstractions/Queries/ICommandQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs index 290c6527..e1de7adb 100644 --- a/src/EntityDb.Abstractions/Queries/ICommandQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs @@ -1,26 +1,26 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Abstractions.Queries; +namespace EntityDb.Abstractions.Sources.Queries; /// -/// Abstracts a query on commands. +/// Abstracts a query on source messages. /// -public interface ICommandQuery : IQuery +public interface IMessageQuery : IQuery { /// - /// Returns a built from a command filter builder. + /// Returns a built from a message filter builder. /// /// The type of filter used by the repository. - /// The command filter builder. + /// The message filter builder. /// A built from . - TFilter GetFilter(ICommandFilterBuilder builder); + TFilter GetFilter(IMessageFilterBuilder builder); /// - /// Returns a built from a command sort builder. + /// Returns a built from a message sort builder. /// /// The type of sort used by the repository. - /// The command sort builder. + /// The message sort builder. /// A built from . - TSort? GetSort(ICommandSortBuilder builder); + TSort? GetSort(IMessageSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Queries/IQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IQuery.cs similarity index 74% rename from src/EntityDb.Abstractions/Queries/IQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IQuery.cs index 5b85d6ac..7d1691f4 100644 --- a/src/EntityDb.Abstractions/Queries/IQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IQuery.cs @@ -1,7 +1,8 @@ -namespace EntityDb.Abstractions.Queries; +namespace EntityDb.Abstractions.Sources.Queries; /// -/// Abstracts a query for an object repository. Possible objects include: agentSignatures, commands, facts, and leases. +/// Abstracts a query for an object repository. Possible objects include: +/// agentSignatures, deltas, facts, leases, and aliases. /// public interface IQuery { diff --git a/src/EntityDb.Abstractions/Queries/ITagQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs similarity index 83% rename from src/EntityDb.Abstractions/Queries/ITagQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs index af5694c1..ee8bd644 100644 --- a/src/EntityDb.Abstractions/Queries/ITagQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Abstractions.Queries; +namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on tags. diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs new file mode 100644 index 00000000..e070a892 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs @@ -0,0 +1,31 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; + +/// +/// Builds a sort for a lease query. +/// +/// The type of sort used by the repository. +public interface ILeaseSortBuilder : IMessageSortBuilder +{ + /// + /// Returns a that orders leases by . + /// + /// Pass true for ascending order or false for descending order. + /// A that orders leases by . + TSort LeaseScope(bool ascending); + + /// + /// Returns a that orders leases by . + /// + /// Pass true for ascending order or false for descending order. + /// A that orders leases by . + TSort LeaseLabel(bool ascending); + + /// + /// Returns a that orders leases by . + /// + /// Pass true for ascending order or false for descending order. + /// A that orders leases by . + TSort LeaseValue(bool ascending); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs new file mode 100644 index 00000000..4e25cab0 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs @@ -0,0 +1,15 @@ +namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; + +/// +/// Builds a for a agentSignature query. +/// +/// The type of sort used by the repository. +public interface IMessageGroupSortBuilder : ISortBuilder +{ + /// + /// Returns a that orders objects by entity ids. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by entity ids. + TSort EntityIds(bool ascending); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs new file mode 100644 index 00000000..ab572cd7 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs @@ -0,0 +1,22 @@ +namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; + +/// +/// Builds a for a source message query. +/// +/// The type of sort used by the repository. +public interface IMessageSortBuilder : ISortBuilder +{ + /// + /// Returns a that orders objects by entity id. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by entity id. + TSort EntityId(bool ascending); + + /// + /// Returns a that orders objects by entity version. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by entity version. + TSort EntityVersion(bool ascending); +} diff --git a/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs similarity index 71% rename from src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs index 2e9b2ff6..38653ab4 100644 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs @@ -1,19 +1,12 @@ -namespace EntityDb.Abstractions.Queries.SortBuilders; +namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; /// -/// Builds a sort for an object repository. Possible objects include: agentSignatures, commands, facts, and leases. +/// Builds a sort for an object repository. Possible objects include: +/// agentSignatures, deltas, facts, leases, and aliases. /// /// The type of sort used by the repository. public interface ISortBuilder { - /// - [Obsolete("Please use SourceTimeStamp instead. This will be removed in a future version.")] - TSort TransactionTimeStamp(bool ascending) => SourceTimeStamp(ascending); - - /// - [Obsolete("Please use SourceId instead. This will be removed in a future version.")] - TSort TransactionId(bool ascending) => SourceId(ascending); - /// /// Returns a that orders objects by source timestamp. /// @@ -28,6 +21,13 @@ public interface ISortBuilder /// A that orders objects by source id. TSort SourceId(bool ascending); + /// + /// Returns a that orders objects by data type. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by data type. + TSort DataType(bool ascending); + /// /// Returns a that orders objects ordered by a series of sorts. /// diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs new file mode 100644 index 00000000..85fa7ff0 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; + +/// +/// Builds a sort for a tag query. +/// +/// The type of sort used by the repository. +public interface ITagSortBuilder : IMessageSortBuilder +{ + /// + /// Returns a that orders tags by . + /// + /// Pass true for ascending order or false for descending order. + /// A that orders tags by . + TSort TagLabel(bool ascending); + + /// + /// Returns a that orders tags by . + /// + /// Pass true for ascending order or false for descending order. + /// A that orders tags by . + TSort TagValue(bool ascending); +} diff --git a/src/EntityDb.Abstractions/Sources/Source.cs b/src/EntityDb.Abstractions/Sources/Source.cs new file mode 100644 index 00000000..ff3eed94 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Source.cs @@ -0,0 +1,30 @@ +using EntityDb.Abstractions.ValueObjects; +using System.Collections.Immutable; + +namespace EntityDb.Abstractions.Sources; + +/// +/// Represents a set of messages that can be committed. +/// +public sealed record Source +{ + /// + /// The id assigned to the source. + /// + public required Id Id { get; init; } + + /// + /// The date and time associated with the source, according to the agent. + /// + public required TimeStamp TimeStamp { get; init; } + + /// + /// The signature of the agent. + /// + public required object AgentSignature { get; init; } + + /// + /// The messages of the source. + /// + public required ImmutableArray Messages { get; init; } +} diff --git a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs b/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs deleted file mode 100644 index 994c39a5..00000000 --- a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs +++ /dev/null @@ -1,55 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Transactions.Builders; - -/// -/// Provides a way to construct an . Note that no operations are permanent until -/// you call and pass the result to a transaction repository. -/// -/// The type of the entity in the transaction. -public interface ITransactionBuilder -{ - /// - /// Returns a associated with a given entity id, if it is known. - /// - /// The id associated with the entity. - /// A associated with , if it is known. - TEntity GetEntity(Id entityId); - - /// - /// Indicates whether or not a associated with a given entity id is in memory. - /// - /// The id of the entity. - /// - /// true if a associated with is in memory, or - /// else false. - /// - bool IsEntityKnown(Id entityId); - - /// - /// Associate a with a given entity id. - /// - /// An id associated with a . - /// A . - /// The transaction builder. - /// - /// Call this method to load an entity that already exists before calling - /// . - /// - ITransactionBuilder Load(Id entityId, TEntity entity); - - /// - /// Adds a single command to the transaction with a given entity id. - /// - /// The id associated with the . - /// The new command that modifies the . - /// The transaction builder. - ITransactionBuilder Append(Id entityId, object command); - - /// - /// Returns a new instance of . - /// - /// A new id for the new transaction. - /// A new instance of . - ITransaction Build(Id transactionId); -} diff --git a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilderFactory.cs b/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilderFactory.cs deleted file mode 100644 index bfedfb7d..00000000 --- a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilderFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Transactions.Builders; - -/// -/// Represents a type used to create instances of or -/// . -/// -/// -public interface ITransactionBuilderFactory -{ - /// - /// Creates a new instance of . - /// - /// The name of the agent signature options. - /// A cancellation token. - /// A new instance of . - Task> Create(string agentSignatureOptionsName, - CancellationToken cancellationToken = default); - - /// - /// Creates a new instance of . - /// - /// The name of the agent signature options. - /// The id of the entity. - /// A cancellation token. - /// A new instance of . - Task> CreateForSingleEntity(string agentSignatureOptionsName, Id entityId, - CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/Transactions/ITransaction.cs b/src/EntityDb.Abstractions/Transactions/ITransaction.cs deleted file mode 100644 index a6d501a3..00000000 --- a/src/EntityDb.Abstractions/Transactions/ITransaction.cs +++ /dev/null @@ -1,16 +0,0 @@ -using EntityDb.Abstractions.Sources; -using System.Collections.Immutable; - -namespace EntityDb.Abstractions.Transactions; - -/// -/// Represents a set of objects which must be committed together or not at all. Possible objects include: -/// agentSignatures, commands, leases, and tags. -/// -public interface ITransaction : ISource -{ - /// - /// A series commands used to modify an entity. - /// - ImmutableArray Commands { get; } -} diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs deleted file mode 100644 index 78c5faa6..00000000 --- a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; -using System.Collections.Immutable; - -namespace EntityDb.Abstractions.Transactions; - -/// -/// Represents a modification to an entity. -/// -public interface ITransactionCommand -{ - /// - /// The id of the entity. - /// - Id EntityId { get; } - - /// - /// The version number associated with this command. - /// - VersionNumber EntityVersionNumber { get; } - - /// - /// The command data. - /// - object Data { get; } - - /// - /// The leases to be added. - /// - ImmutableArray AddLeases { get; } - - /// - /// The tags to be added. - /// - ImmutableArray AddTags { get; } - - /// - /// The leases to be deleted. - /// - ImmutableArray DeleteLeases { get; } - - /// - /// The tags to be deleted. - /// - ImmutableArray DeleteTags { get; } - - /// - [Obsolete("Please use Data instead. This will be removed in a future version.")] - object Command => Data; -} diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionRepository.cs b/src/EntityDb.Abstractions/Transactions/ITransactionRepository.cs deleted file mode 100644 index 0ccc7901..00000000 --- a/src/EntityDb.Abstractions/Transactions/ITransactionRepository.cs +++ /dev/null @@ -1,148 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Transactions; - -/// -/// Represents an explicit set of objects which represent a complete history of a set of entities. -/// -public interface ITransactionRepository : IDisposableResource -{ - /// - /// Returns the transaction ids which are found by a agentSignature query. - /// - /// The agentSignature query. - /// A cancellation token. - /// The transaction ids which are found by . - IAsyncEnumerable EnumerateTransactionIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the transaction ids which are found by a command query. - /// - /// The command query. - /// A cancellation token. - /// The transaction ids which are found by . - IAsyncEnumerable EnumerateTransactionIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the transaction ids which are found by a lease query. - /// - /// The lease query. - /// A cancellation token. - /// The transaction ids which are found by . - IAsyncEnumerable EnumerateTransactionIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the transaction ids which are found by a tag query. - /// - /// The tag query. - /// A cancellation token. - /// The transaction ids which are found by . - IAsyncEnumerable EnumerateTransactionIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the entity ids which are found by a agentSignature query. - /// - /// The agentSignature query. - /// A cancellation token. - /// The entity ids which are found by . - IAsyncEnumerable EnumerateEntityIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the entity ids which are found by a command query. - /// - /// The command query. - /// A cancellation token. - /// The entity ids which are found by . - IAsyncEnumerable EnumerateEntityIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the entity ids which are found by a lease query. - /// - /// The lease query. - /// A cancellation token. - /// The entity ids which are found by . - IAsyncEnumerable EnumerateEntityIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the entity ids which are found by a tag query. - /// - /// The tag query. - /// A cancellation token. - /// The entity ids which are found by . - IAsyncEnumerable EnumerateEntityIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the agentSignatures which are found by a agentSignature query. - /// - /// The agentSignature query. - /// A cancellation token. - /// The agentSignatures which are found by . - IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the commands which are found by a command query. - /// - /// The command query. - /// A cancellation token. - /// The commands which are found by . - IAsyncEnumerable EnumerateCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the leases which are found by a lease query. - /// - /// The lease query. - /// A cancellation token. - /// The leases which are found by . - IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the tags which are found by a tag query. - /// - /// The tag query. - /// A cancellation token. - /// The tags which are found by . - IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the annotated agent signatures which are found by an agent signature query. - /// - /// The agent signature query. - /// A cancellation token. - /// The annotated agent signatures which are found by . - IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default); - - /// - /// Returns the annotated commands which are found by a command query. - /// - /// The command query. - /// A cancellation token. - /// The annotated commands which are found by . - IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default); - - /// - /// Inserts a single transaction with an atomic commit. - /// - /// The transaction. - /// A cancellation token. - /// true if the insert succeeded, or false if the insert failed. - Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionRepositoryFactory.cs b/src/EntityDb.Abstractions/Transactions/ITransactionRepositoryFactory.cs deleted file mode 100644 index 18dc6d0a..00000000 --- a/src/EntityDb.Abstractions/Transactions/ITransactionRepositoryFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Disposables; - -namespace EntityDb.Abstractions.Transactions; - -/// -/// Represents a type used to create instances of . -/// -public interface ITransactionRepositoryFactory : IDisposableResource -{ - /// - /// Creates a new instance of . - /// - /// The agent's use case for the repository. - /// A cancellation token. - /// A new instance of . - Task CreateRepository(string transactionSessionOptionsName, - CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/ValueObjects/Id.cs b/src/EntityDb.Abstractions/ValueObjects/Id.cs index 200b26e5..6fab313f 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Id.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Id.cs @@ -1,7 +1,7 @@ namespace EntityDb.Abstractions.ValueObjects; /// -/// Represents a unique identifier for an object. +/// Represents an identifier for an entity or projection. /// /// The backing value. public readonly record struct Id(Guid Value) @@ -15,22 +15,7 @@ public static Id NewId() return new Id(Guid.NewGuid()); } - /// - /// Returns a string representation of the value of this instance in - /// registry format. - /// - /// - /// The value of this , formatted by using the "D" - /// format specifier as follows: - /// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - /// where the value of the Id is represented as a series of lowercase - /// hexadecimal digits in groups of 8, 4, 4, 4, and 12 digits and - /// separated by hyphens. An example of a return value is - /// "382c74c3-721d-4f34-80e5-57657b6cbc27". To convert the hexadecimal - /// digits from a through f to uppercase, call the - /// method on the returned - /// string. - /// + /// public override string ToString() { return Value.ToString(); @@ -42,17 +27,17 @@ public override string ToString() /// The implicit id argument. public static implicit operator Pointer(Id id) { - return id + VersionNumber.MinValue; + return id + Version.Zero; } /// - /// Combine an Id and a VersionNumber into a Pointer. + /// Combine an Id and a Version into a Pointer. /// /// - /// - /// A pointer for the id and version number. - public static Pointer operator +(Id id, VersionNumber versionNumber) + /// + /// A pointer for the id and version. + public static Pointer operator +(Id id, Version version) { - return new Pointer(id, versionNumber); + return new Pointer(id, version); } } diff --git a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs b/src/EntityDb.Abstractions/ValueObjects/Pointer.cs index 599cd904..0d0ce9da 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Pointer.cs @@ -1,24 +1,48 @@ namespace EntityDb.Abstractions.ValueObjects; /// -/// Points to an object (e.g., entity, projection) +/// Points to an entity or projection /// -/// The id of the object. -/// The version number of the object. -public readonly record struct Pointer(Id Id, VersionNumber VersionNumber) +/// The id of the entity or projection. +/// The version of the entity or projection. +public readonly record struct Pointer(Id Id, Version Version) { /// - /// Checks if the version number found satisfies the pointer. + /// Prints out {Id}@{Version}. + /// See and /// - /// The actual version number found via queries. + /// + public override string ToString() + { + return $"{Id}@{Version}"; + } + + /// + /// Checks if the pointer found satisfies the pointer. + /// + /// The actual version found via queries. /// true if - public bool IsSatisfiedBy(VersionNumber actualVersionNumber) + public bool IsSatisfiedBy(Pointer actualPointer) { - if (VersionNumber == VersionNumber.MinValue) + if (Id != actualPointer.Id) { - return actualVersionNumber != VersionNumber.MinValue; + return false; } - return actualVersionNumber == VersionNumber; + if (Version == Version.Zero) + { + return actualPointer.Version != Version.Zero; + } + + return actualPointer.Version == Version; + } + + /// + /// Returns the next pointer. + /// + /// The next pointer. + public Pointer Next() + { + return Id + Version.Next(); } } diff --git a/src/EntityDb.Abstractions/ValueObjects/Version.cs b/src/EntityDb.Abstractions/ValueObjects/Version.cs new file mode 100644 index 00000000..d15e34d3 --- /dev/null +++ b/src/EntityDb.Abstractions/ValueObjects/Version.cs @@ -0,0 +1,35 @@ +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; + +namespace EntityDb.Abstractions.ValueObjects; + +/// +/// Represents a version for an entity or projection. +/// +/// The backing value. +public readonly record struct Version(ulong Value) +{ + /// + /// This constant represents the minimum possible version. + /// In the context of an , + /// this value is reserved for auto-increment behavior. + /// In the context of an , + /// this value is reserved to point to the latest snapshot. + /// + public static readonly Version Zero = new(ulong.MinValue); + + /// + /// Returns the next version. + /// + /// The next version. + public Version Next() + { + return new Version(Value + 1); + } + + /// + public override string ToString() + { + return Value.ToString(); + } +} diff --git a/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs b/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs deleted file mode 100644 index ee81fc0e..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs +++ /dev/null @@ -1,51 +0,0 @@ -using EntityDb.Abstractions.Transactions; - -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// Represents a particular version for an object. -/// -/// The backing value. -public readonly record struct VersionNumber(ulong Value) -{ - /// - /// This constant represents the minimum possible version number. - /// In the context of an , - /// this value is reserved to indicate there is no previous version number. - /// In the context of an , - /// this value is reserved to point to the latest snapshot. - /// - public static readonly VersionNumber MinValue = new(ulong.MinValue); - - /// - /// Gets the next version number. - /// - /// The next version number. - public VersionNumber Next() - { - return new VersionNumber(Value + 1); - } - - /// - /// Gets the previous version number. - /// - /// The previous version number. - public VersionNumber Previous() - { - return new VersionNumber(Value - 1); - } - - /// - /// Converts the numeric value of this instance to its equivalent string - /// representation. - /// - /// - /// The string representation of the value of this instance, consisting - /// of a sequence of digits ranging from 0 to 9, without a sign or - /// leading zeroes. - /// - public override string ToString() - { - return Value.ToString(); - } -} diff --git a/src/EntityDb.Common/Annotations/EntitiesAnnotation.cs b/src/EntityDb.Common/Annotations/EntitiesAnnotation.cs deleted file mode 100644 index e765b92a..00000000 --- a/src/EntityDb.Common/Annotations/EntitiesAnnotation.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Annotations; - -internal record EntitiesAnnotation -( - Id TransactionId, - TimeStamp TransactionTimeStamp, - Id[] EntityIds, - TData Data -) : IEntitiesAnnotation -{ - public static IEntitiesAnnotation CreateFromBoxedData - ( - Id transactionId, - TimeStamp transactionTimeStamp, - Id[] entityIds, - object boxedData - ) - { - var dataAnnotationType = typeof(EntitiesAnnotation<>).MakeGenericType(boxedData.GetType()); - - return (IEntitiesAnnotation)Activator.CreateInstance - ( - dataAnnotationType, - transactionId, - transactionTimeStamp, - entityIds, - boxedData - )!; - } -} diff --git a/src/EntityDb.Common/Annotations/EntityAnnotation.cs b/src/EntityDb.Common/Annotations/EntityAnnotation.cs deleted file mode 100644 index 597f0d8f..00000000 --- a/src/EntityDb.Common/Annotations/EntityAnnotation.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Annotations; - -internal record EntityAnnotation -( - Id TransactionId, - TimeStamp TransactionTimeStamp, - Id EntityId, - VersionNumber EntityVersionNumber, - TData Data -) : IEntityAnnotation -{ - public static IEntityAnnotation CreateFromBoxedData - ( - Id transactionId, - TimeStamp transactionTimeStamp, - Id entityId, - VersionNumber entityVersionNumber, - object boxedData - ) - { - var dataAnnotationType = typeof(EntityAnnotation<>).MakeGenericType(boxedData.GetType()); - - return (IEntityAnnotation)Activator.CreateInstance - ( - dataAnnotationType, - transactionId, - transactionTimeStamp, - entityId, - entityVersionNumber, - boxedData - )!; - } -} diff --git a/src/EntityDb.Common/Documents/IEntitiesDocument.cs b/src/EntityDb.Common/Documents/IEntitiesDocument.cs deleted file mode 100644 index 3ba5cfe5..00000000 --- a/src/EntityDb.Common/Documents/IEntitiesDocument.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Documents; - -internal interface IEntitiesDocument : ITransactionDocument -{ - Id[] EntityIds { get; } -} diff --git a/src/EntityDb.Common/Documents/IEntityDocument.cs b/src/EntityDb.Common/Documents/IEntityDocument.cs deleted file mode 100644 index d6ae722c..00000000 --- a/src/EntityDb.Common/Documents/IEntityDocument.cs +++ /dev/null @@ -1,9 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Documents; - -internal interface IEntityDocument : ITransactionDocument -{ - Id EntityId { get; } - VersionNumber EntityVersionNumber { get; } -} diff --git a/src/EntityDb.Common/Documents/ITransactionDocument.cs b/src/EntityDb.Common/Documents/ITransactionDocument.cs deleted file mode 100644 index 8008b49b..00000000 --- a/src/EntityDb.Common/Documents/ITransactionDocument.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Documents; - -internal interface ITransactionDocument -{ - Id TransactionId { get; } - - TimeStamp TransactionTimeStamp { get; } - - string DataType { get; } - - TSerializedData Data { get; } -} diff --git a/src/EntityDb.Common/Entities/EntityRepository.cs b/src/EntityDb.Common/Entities/EntityRepository.cs index b3a6d8f1..b2ac908d 100644 --- a/src/EntityDb.Common/Entities/EntityRepository.cs +++ b/src/EntityDb.Common/Entities/EntityRepository.cs @@ -1,11 +1,10 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; -using EntityDb.Common.Queries; +using EntityDb.Common.Sources.Queries.Standard; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.Common.Entities; @@ -18,16 +17,16 @@ internal class EntityRepository : DisposableResourceBaseClass, IEntityR public EntityRepository ( IEnumerable sourceSubscribers, - ITransactionRepository transactionRepository, + ISourceRepository sourceRepository, ISnapshotRepository? snapshotRepository = null ) { _sourceSubscribers = sourceSubscribers; - TransactionRepository = transactionRepository; + SourceRepository = sourceRepository; SnapshotRepository = snapshotRepository; } - public ITransactionRepository TransactionRepository { get; } + public ISourceRepository SourceRepository { get; } public ISnapshotRepository? SnapshotRepository { get; } public async Task GetSnapshot(Pointer entityPointer, CancellationToken cancellationToken = default) @@ -37,13 +36,21 @@ public async Task GetSnapshot(Pointer entityPointer, CancellationToken TEntity.Construct(entityPointer.Id) : TEntity.Construct(entityPointer.Id); - var commandQuery = new GetEntityCommandsQuery(entityPointer, snapshot.GetVersionNumber()); + var snapshotPointer = snapshot.GetPointer(); - var commands = TransactionRepository.EnumerateCommands(commandQuery, cancellationToken); + var query = new GetDeltasQuery(entityPointer, snapshotPointer.Version); - var entity = await commands.AggregateAsync(snapshot, (current, command) => current.Reduce(command), cancellationToken: cancellationToken); + var deltas = SourceRepository.EnumerateDeltas(query, cancellationToken); - if (!entityPointer.IsSatisfiedBy(entity.GetVersionNumber())) + var entity = await deltas + .AggregateAsync + ( + snapshot, + (current, delta) => current.Reduce(delta), + cancellationToken + ); + + if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) { throw new SnapshotPointerDoesNotExistException(); } @@ -51,13 +58,14 @@ public async Task GetSnapshot(Pointer entityPointer, CancellationToken return entity; } - public async Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default) + public async Task Commit(Source source, + CancellationToken cancellationToken = default) { - var success = await TransactionRepository.PutTransaction(transaction, cancellationToken); + var success = await SourceRepository.Commit(source, cancellationToken); if (success) { - Publish(transaction); + Publish(source); } return success; @@ -65,7 +73,7 @@ public async Task PutTransaction(ITransaction transaction, CancellationTok public override async ValueTask DisposeAsync() { - await TransactionRepository.DisposeAsync(); + await SourceRepository.DisposeAsync(); if (SnapshotRepository is not null) { @@ -73,28 +81,28 @@ public override async ValueTask DisposeAsync() } } - private void Publish(ITransaction transaction) + private void Publish(Source source) { foreach (var sourceSubscriber in _sourceSubscribers) { - sourceSubscriber.Notify(transaction); + sourceSubscriber.Notify(source); } } public static EntityRepository Create ( IServiceProvider serviceProvider, - ITransactionRepository transactionRepository, + ISourceRepository sourceRepository, ISnapshotRepository? snapshotRepository = null ) { if (snapshotRepository is null) { return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionRepository); + sourceRepository); } - return ActivatorUtilities.CreateInstance>(serviceProvider, transactionRepository, + return ActivatorUtilities.CreateInstance>(serviceProvider, sourceRepository, snapshotRepository); } } diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index 0519305c..96a996b4 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; namespace EntityDb.Common.Entities; @@ -9,36 +9,36 @@ internal class EntityRepositoryFactory : IEntityRepositoryFactory? _snapshotRepositoryFactory; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; public EntityRepositoryFactory ( IServiceProvider serviceProvider, - ITransactionRepositoryFactory transactionRepositoryFactory, + ISourceRepositoryFactory sourceRepositoryFactory, ISnapshotRepositoryFactory? snapshotRepositoryFactory = null ) { _serviceProvider = serviceProvider; - _transactionRepositoryFactory = transactionRepositoryFactory; + _sourceRepositoryFactory = sourceRepositoryFactory; _snapshotRepositoryFactory = snapshotRepositoryFactory; } - public async Task> CreateRepository(string transactionSessionOptionsName, + public async Task> CreateRepository(string sourceSessionOptionsName, string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default) { - var transactionRepository = - await _transactionRepositoryFactory.CreateRepository(transactionSessionOptionsName, cancellationToken); + var sourceRepository = + await _sourceRepositoryFactory.CreateRepository(sourceSessionOptionsName, cancellationToken); if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) { return EntityRepository.Create(_serviceProvider, - transactionRepository); + sourceRepository); } var snapshotRepository = await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); return EntityRepository.Create(_serviceProvider, - transactionRepository, snapshotRepository); + sourceRepository, snapshotRepository); } } diff --git a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs new file mode 100644 index 00000000..749d63ab --- /dev/null +++ b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs @@ -0,0 +1,101 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Exceptions; +using System.Collections.Immutable; + +namespace EntityDb.Common.Entities; + +internal sealed class EntitySourceBuilder : IEntitySourceBuilder + where TEntity : IEntity +{ + private readonly IAgent _agent; + private readonly Dictionary _knownEntities = new(); + private readonly List _sourceMessages = new(); + + public EntitySourceBuilder(IAgent agent) + { + _agent = agent; + } + + public TEntity GetEntity(Id entityId) + { + return _knownEntities[entityId]; + } + + public bool IsEntityKnown(Id entityId) + { + return _knownEntities.ContainsKey(entityId); + } + + public IEntitySourceBuilder Load(Id entityId, TEntity entity) + { + if (IsEntityKnown(entityId)) + { + throw new EntityBranchAlreadyLoadedException(); + } + + _knownEntities.Add(entityId, entity); + + return this; + } + + public IEntitySourceBuilder Append(Id entityId, object delta) + { + ConstructIfNotKnown(entityId); + + var entity = _knownEntities[entityId].Reduce(delta); + + _sourceMessages.Add(new Message + { + EntityPointer = entity.GetPointer(), + Delta = delta, + AddLeases = delta is IAddLeasesDelta addLeasesDelta + ? addLeasesDelta.GetLeases(entity).ToImmutableArray() + : ImmutableArray.Empty, + AddTags = delta is IAddTagsDelta addTagsDelta + ? addTagsDelta.GetTags(entity).ToImmutableArray() + : ImmutableArray.Empty, + DeleteLeases = delta is IDeleteLeasesDelta deleteLeasesDelta + ? deleteLeasesDelta.GetLeases(entity).ToImmutableArray() + : ImmutableArray.Empty, + DeleteTags = delta is IDeleteTagsDelta deleteTagsDelta + ? deleteTagsDelta.GetTags(entity).ToImmutableArray() + : ImmutableArray.Empty, + }); + + _knownEntities[entityId] = entity; + + return this; + } + + public Source Build(Id sourceId) + { + var source = new Source + { + Id = sourceId, + TimeStamp = _agent.TimeStamp, + AgentSignature = _agent.Signature, + Messages = _sourceMessages.ToImmutableArray(), + }; + + _sourceMessages.Clear(); + + return source; + } + + private void ConstructIfNotKnown(Id entityId) + { + if (IsEntityKnown(entityId)) + { + return; + } + + var entity = TEntity.Construct(entityId); + + _knownEntities.Add(entityId, entity); + } +} diff --git a/src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs b/src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs new file mode 100644 index 00000000..2501bd75 --- /dev/null +++ b/src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Entities; + +internal sealed class EntitySourceBuilderFactory : IEntitySourceBuilderFactory + where TEntity : IEntity +{ + private readonly IAgentAccessor _agentAccessor; + + public EntitySourceBuilderFactory(IAgentAccessor agentAccessor) + { + _agentAccessor = agentAccessor; + } + + public async Task> Create(string agentSignatureOptionsName, + CancellationToken cancellationToken) + { + var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); + + return new EntitySourceBuilder(agent); + } + + public async Task> CreateForSingleEntity(string agentSignatureOptionsName, + Id entityId, CancellationToken cancellationToken) + { + var sourceBuilder = await Create(agentSignatureOptionsName, cancellationToken); + + return new SingleEntitySourceBuilder(sourceBuilder, entityId); + } +} diff --git a/src/EntityDb.Common/Entities/IEntity.cs b/src/EntityDb.Common/Entities/IEntity.cs deleted file mode 100644 index 3edb98f4..00000000 --- a/src/EntityDb.Common/Entities/IEntity.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Common.Snapshots; - -namespace EntityDb.Common.Entities; - -/// -/// Indicates the entity is compatible with several EntityDb.Common implementations. -/// -/// The type of the entity. -public interface IEntity : ISnapshot -{ - /// - /// Returns true if is not expected to throw an exception. - /// - /// The command - /// true if is not expected to throw an exception. - static abstract bool CanReduce(object command); - - /// - /// Returns a new that incorporates the commands. - /// - /// The command - /// A new that incorporates . - TEntity Reduce(object command); -} diff --git a/src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs b/src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs new file mode 100644 index 00000000..1ee59eb7 --- /dev/null +++ b/src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs @@ -0,0 +1,48 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Entities; + +internal sealed class SingleEntitySourceBuilder : ISingleEntitySourceBuilder + where TEntity : IEntity +{ + private readonly IEntitySourceBuilder _sourceBuilder; + + internal SingleEntitySourceBuilder(IEntitySourceBuilder sourceBuilder, Id entityId) + { + _sourceBuilder = sourceBuilder; + EntityId = entityId; + } + + public Id EntityId { get; } + + public TEntity GetEntity() + { + return _sourceBuilder.GetEntity(EntityId); + } + + public bool IsEntityKnown() + { + return _sourceBuilder.IsEntityKnown(EntityId); + } + + public ISingleEntitySourceBuilder Load(TEntity entity) + { + _sourceBuilder.Load(EntityId, entity); + + return this; + } + + public ISingleEntitySourceBuilder Append(object delta) + { + _sourceBuilder.Append(EntityId, delta); + + return this; + } + + public Source Build(Id sourceId) + { + return _sourceBuilder.Build(sourceId); + } +} diff --git a/src/EntityDb.Common/EntityDb.Common.csproj b/src/EntityDb.Common/EntityDb.Common.csproj index 1019ed9e..0fb988bd 100644 --- a/src/EntityDb.Common/EntityDb.Common.csproj +++ b/src/EntityDb.Common/EntityDb.Common.csproj @@ -7,7 +7,8 @@ - + + diff --git a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs b/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs index c832c704..a19ab846 100644 --- a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs +++ b/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs @@ -1,10 +1,10 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; namespace EntityDb.Common.Exceptions; /// -/// The exception that is thrown when an actor passes a to an -/// that was created for read-only mode. +/// The exception that is thrown when an actor passes a to an +/// that was created for read-only mode. /// public sealed class CannotWriteInReadOnlyModeException : Exception { diff --git a/src/EntityDb.Common/Exceptions/DeserializeException.cs b/src/EntityDb.Common/Exceptions/DeserializeException.cs index ce345af7..a13480c0 100644 --- a/src/EntityDb.Common/Exceptions/DeserializeException.cs +++ b/src/EntityDb.Common/Exceptions/DeserializeException.cs @@ -2,8 +2,7 @@ /// /// The exception that is thrown when an object envelope cannot be deserialized. Possible objects include: -/// agentSignatures, -/// commands, facts, and leases. +/// agentSignatures, deltas, facts, leases, and aliases. /// public sealed class DeserializeException : Exception { diff --git a/src/EntityDb.Common/Exceptions/EntityAlreadyKnownException.cs b/src/EntityDb.Common/Exceptions/EntityAlreadyKnownException.cs deleted file mode 100644 index 0aa2967a..00000000 --- a/src/EntityDb.Common/Exceptions/EntityAlreadyKnownException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Transactions.Builders; - -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an actor passes an entity id to -/// -/// with an entity id that has already been loaded. -/// -public sealed class EntityAlreadyKnownException : Exception -{ -} diff --git a/src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs b/src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs new file mode 100644 index 00000000..29d5e43e --- /dev/null +++ b/src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs @@ -0,0 +1,12 @@ +using EntityDb.Common.Entities; + +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when an actor passes an entity branch to +/// +/// with an entity branch that has already been loaded. +/// +public sealed class EntityBranchAlreadyLoadedException : Exception +{ +} diff --git a/src/EntityDb.Common/Exceptions/NoAgentException.cs b/src/EntityDb.Common/Exceptions/NoAgentException.cs index 0cfb39f8..93d9c2fa 100644 --- a/src/EntityDb.Common/Exceptions/NoAgentException.cs +++ b/src/EntityDb.Common/Exceptions/NoAgentException.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; namespace EntityDb.Common.Exceptions; diff --git a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs index 17d99fee..68a0c143 100644 --- a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs +++ b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs @@ -1,32 +1,37 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.ValueObjects; using Microsoft.Extensions.Logging; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Exceptions; /// -/// The exception that is thrown when an actor passes an to -/// with any -/// where the value of -/// is not equal to of the committed previous version number. +/// The exception that is thrown when an actor passes an +/// to +/// with any +/// where the value of +/// in +/// +/// is not equal to +/// of the committed previous version. /// /// /// A program will not be able to catch this exception if it is thrown. -/// will return false, and this +/// will return false, and this /// exception will be logged using the injected . /// public sealed class OptimisticConcurrencyException : Exception { /// - /// Throws a new if - /// is not equal to . + /// Throws a new if + /// is not equal to . /// - /// The expected previous version number. - /// The actual previous version number. - public static void ThrowIfMismatch(VersionNumber expectedPreviousVersionNumber, - VersionNumber actualPreviousVersionNumber) + /// The expected previous version. + /// The actual previous version. + public static void ThrowIfMismatch(Version expectedPreviousVersion, + Version actualPreviousVersion) { - if (expectedPreviousVersionNumber != actualPreviousVersionNumber) + if (expectedPreviousVersion != actualPreviousVersion) { throw new OptimisticConcurrencyException(); } diff --git a/src/EntityDb.Common/Exceptions/SerializeException.cs b/src/EntityDb.Common/Exceptions/SerializeException.cs index 5b807cdb..9407aa6c 100644 --- a/src/EntityDb.Common/Exceptions/SerializeException.cs +++ b/src/EntityDb.Common/Exceptions/SerializeException.cs @@ -2,8 +2,7 @@ /// /// The exception that is thrown when an object envelope cannot be serialized. Possible objects include: -/// agentSignatures, -/// commands, leases, and tags. +/// agentSignatures, deltas, leases, tags, and aliases. /// public sealed class SerializeException : Exception { diff --git a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs b/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs deleted file mode 100644 index 9799d3cf..00000000 --- a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an actor passes an to -/// with any -/// where the value of -/// is equal to 0. -/// -/// -/// Version Zero is reserved for an entity that has not yet been created/persisted. -/// -public sealed class VersionZeroReservedException : Exception -{ - /// - /// Throws a new if is - /// equal to zero. - /// - /// - public static void ThrowIfZero(VersionNumber versionNumber) - { - if (versionNumber == VersionNumber.MinValue) - { - throw new VersionZeroReservedException(); - } - } -} diff --git a/src/EntityDb.Common/Extensions/PointerExtensions.cs b/src/EntityDb.Common/Extensions/PointerExtensions.cs deleted file mode 100644 index 873fff98..00000000 --- a/src/EntityDb.Common/Extensions/PointerExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Extensions; - -internal static class PointerExtensions -{ - public static Pointer Previous(this Pointer pointer) - { - return pointer.Id + pointer.VersionNumber.Previous(); - } -} diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 8f2b23a0..7e3353ef 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -1,15 +1,13 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Transactions.Builders; +using EntityDb.Abstractions.Sources.Agents; using EntityDb.Common.Entities; using EntityDb.Common.Projections; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; using EntityDb.Common.Sources.ReprocessorQueues; using EntityDb.Common.Sources.Subscribers; -using EntityDb.Common.Transactions.Builders; using EntityDb.Common.TypeResolvers; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; @@ -29,7 +27,8 @@ internal static void Add(this IServiceCollection serviceCollection, Se serviceCollection.Add(new ServiceDescriptor(typeof(TService), serviceFactory, serviceLifetime)); } - internal static void Add(this IServiceCollection serviceCollection, ServiceLifetime serviceLifetime) + internal static void Add(this IServiceCollection serviceCollection, + ServiceLifetime serviceLifetime) where TService : class where TImplementation : TService { @@ -42,16 +41,8 @@ internal static void Add(this IServiceCollection serviceCollection, Se serviceCollection.Add(new ServiceDescriptor(typeof(TService), typeof(TService), serviceLifetime)); } - /// - [Obsolete("Please register your TTransactionProcessor yourself. You may use any scope you want. You will need to call services.AddSourceProcessorQueue(), and you may enqueue source processing by injecting ISourceProcessorQueue and calling Enqueue. There is a generic extension method available if you don't want to implement ISourceProcessorQueueItem.", true)] - public static void AddTransactionProcessorSubscriber(this IServiceCollection serviceCollection, - bool testMode, Func transactionProcessorFactory) - { - throw new NotImplementedException(); - } - /// - /// Registers a queue for processing sources (e.g., transactions) as they are committed. + /// Registers a queue for processing sources as they are committed. /// For test mode, the queue is not actually a queue and will immediately process the source. /// For non-test mode, the queue uses a buffer block. /// @@ -78,7 +69,7 @@ public static void AddSourceProcessorQueue(this IServiceCollection serviceCollec } /// - /// Registers a queue for re-processing sources (e.g., transactions) after they have + /// Registers a queue for re-processing sources after they have /// already been committed (and potentially processed before). For test mode, the queue is /// not actually a queue and will immediately reprocess sources. For non-test mode, the /// queue uses a buffer block. @@ -90,17 +81,17 @@ public static void AddSourceReprocessorQueue(this IServiceCollection serviceColl { if (testMode) { - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); } else { - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddHostedService(serviceProvider => - serviceProvider.GetRequiredService()); + serviceProvider.GetRequiredService()); - serviceCollection.AddSingleton(serviceProvider => - serviceProvider.GetRequiredService()); + serviceCollection.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); } } @@ -152,7 +143,7 @@ public static void AddAgentAccessor(this IServiceCollection serv } /// - /// Adds a transient and a transient implementation of + /// Adds a transient and a transient implementation of /// to a service collection. /// /// The service collection. @@ -160,33 +151,27 @@ public static void AddAgentAccessor(this IServiceCollection serv public static void AddEntity(this IServiceCollection serviceCollection) where TEntity : IEntity { - serviceCollection.AddTransient, TransactionBuilderFactory>(); + serviceCollection + .AddTransient, EntitySourceBuilderFactory>(); serviceCollection.AddTransient, EntityRepositoryFactory>(); } - /// - [Obsolete("Please use AddEntitySnapshotSourceSubscriber instead. This will be removed in a future version.")] - public static void AddEntitySnapshotTransactionSubscriber(this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName) - where TEntity : IEntity - { - serviceCollection.AddEntitySnapshotSourceSubscriber(transactionSessionOptionsName, snapshotSessionOptionsName); - } - /// /// Adds a source subscriber that records snapshots of entities. /// /// The service collection. - /// The agent's intent for the transaction repository. + /// The agent's intent for the source repository. /// The agent's intent for the snapshot repository. /// The type of the entity. public static void AddEntitySnapshotSourceSubscriber(this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName) + string sourceSessionOptionsName, string snapshotSessionOptionsName) where TEntity : IEntity { serviceCollection.AddSingleton>(); - serviceCollection.AddScoped(serviceProvider => EntitySnapshotSourceProcessor.Create(serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); + serviceCollection.AddScoped(serviceProvider => + EntitySnapshotSourceProcessor.Create(serviceProvider, sourceSessionOptionsName, + snapshotSessionOptionsName)); } /// @@ -202,30 +187,19 @@ public static void AddProjection( .AddTransient, ProjectionRepositoryFactory>(); } - /// - [Obsolete("Please use AddProjectionSnapshotSourceSubscriber instead. This will be removed in a future version.")] - public static void AddProjectionSnapshotTransactionSubscriber( - this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName) - where TProjection : IProjection - { - serviceCollection.AddProjectionSnapshotSourceSubscriber(transactionSessionOptionsName, snapshotSessionOptionsName); - } - /// - /// Adds a transaction subscriber that records snapshots of projections. + /// Adds a source subscriber that records snapshots of projections. /// /// The service collection. - /// The agent's intent for the transaction repository. /// The agent's intent for the snapshot repository. /// The type of the projection. public static void AddProjectionSnapshotSourceSubscriber( this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName) + string snapshotSessionOptionsName) where TProjection : IProjection { - serviceCollection.AddSingleton>(); - serviceCollection.AddScoped(serviceProvider => ProjectionSnapshotSourceProcessor.Create(serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); + serviceCollection.AddScoped(serviceProvider => + ProjectionSnapshotSourceProcessor.Create(serviceProvider, snapshotSessionOptionsName)); } } diff --git a/src/EntityDb.Common/Extensions/SnapshotExtensions.cs b/src/EntityDb.Common/Extensions/SnapshotExtensions.cs deleted file mode 100644 index a49f96b3..00000000 --- a/src/EntityDb.Common/Extensions/SnapshotExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Snapshots; - -namespace EntityDb.Common.Extensions; - -internal static class SnapshotExtensions -{ - public static Pointer GetPointer(this TSnapshot snapshot) - where TSnapshot : ISnapshot - { - return snapshot.GetId() + snapshot.GetVersionNumber(); - } -} diff --git a/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs b/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs index 7bb1222a..6aaef704 100644 --- a/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs +++ b/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Common.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Common.Sources.Queries.SortBuilders; namespace EntityDb.Common.Extensions; @@ -9,35 +9,33 @@ namespace EntityDb.Common.Extensions; public static class SortBuilderExtensions { /// - /// Returns a that orders agentSignatures in the reverse order of - /// another - /// . + /// Returns a that orders objects in the reverse order of + /// another . /// /// The type of sort used by the repository. - /// The agentSignature sort builder. + /// The message group sort builder. /// - /// A that orders agentSignatures in the reverse order of - /// . + /// A that orders objects in the reverse order of + /// . /// - public static IAgentSignatureSortBuilder Reverse( - this IAgentSignatureSortBuilder agentSignatureSortBuilder) + public static IMessageGroupSortBuilder Reverse(this IMessageGroupSortBuilder builder) { - return new AgentSignatureReverseSortBuilder(agentSignatureSortBuilder); + return new ReverseMessageGroupSortBuilder(builder); } /// - /// Returns a that orders commands in the reverse order of another - /// . + /// Returns a that orders objects in the reverse order of another + /// . /// /// The type of sort used by the repository. - /// The command sort builder. + /// The message sort builder. /// - /// A that orders commands in the reverse order of - /// . + /// A that orders message in the reverse order of + /// . /// - public static ICommandSortBuilder Reverse(this ICommandSortBuilder commandSortBuilder) + public static IMessageSortBuilder Reverse(this IMessageSortBuilder builder) { - return new CommandReverseSortBuilder(commandSortBuilder); + return new ReverseMessageSortBuilder(builder); } /// @@ -45,14 +43,14 @@ public static ICommandSortBuilder Reverse(this ICommandSortBuilder /// . /// /// The type of sort used by the repository. - /// The lease sort builder. + /// The lease sort builder. /// /// A that orders leases in the reverse order of - /// . + /// . /// - public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder leaseSortBuilder) + public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder builder) { - return new LeaseReverseSortBuilder(leaseSortBuilder); + return new ReverseLeaseSortBuilder(builder); } /// @@ -60,13 +58,13 @@ public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder. /// /// The type of sort used by the repository. - /// The tag sort builder. + /// The tag sort builder. /// /// A that orders tags in the reverse order of - /// . + /// . /// - public static ITagSortBuilder Reverse(this ITagSortBuilder tagSortBuilder) + public static ITagSortBuilder Reverse(this ITagSortBuilder builder) { - return new TagReverseSortBuilder(tagSortBuilder); + return new ReverseTagSortBuilder(builder); } } diff --git a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs b/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs index 643af391..d7ce7a2e 100644 --- a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs +++ b/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs @@ -5,22 +5,22 @@ namespace EntityDb.Common.Extensions; /// -/// Extensions for . +/// Extensions for . /// public static class SourceProcessorQueueExtensions { /// - /// Adds a transaction and the corresponding processor to the queue. + /// Adds a source and the corresponding processor to the queue. /// - /// The type of the - /// The transaction processor queue - /// The transaction to process - public static void Enqueue(this ISourceProcessorQueue sourceProcessorQueue, ISource source) where TSourceProcessor : ISourceProcessor + /// The type of the + /// The source processor queue + /// The source to process + public static void Enqueue(this ISourceProcessorQueue sourceProcessorQueue, Source source) + where TSourceProcessor : ISourceProcessor { sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem { - SourceProcessorType = typeof(TSourceProcessor), - Source = source, + SourceProcessorType = typeof(TSourceProcessor), Source = source, }); } } diff --git a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs new file mode 100644 index 00000000..c26f8314 --- /dev/null +++ b/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs @@ -0,0 +1,89 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Queries.Standard; +using System.Collections.Immutable; + +namespace EntityDb.Common.Extensions; + +/// +/// Extensions for . +/// +public static class SourceRepositoryExtensions +{ + /// + /// Enumerate source ids for any supported query type + /// + /// The source repository + /// The query + /// A cancellation token + /// The source id which are found by + public static IAsyncEnumerable EnumerateSourceIds(this ISourceRepository sourceRepository, + IQuery query, CancellationToken cancellationToken = default) + { + return query switch + { + IMessageGroupQuery messageGroupQuery => sourceRepository.EnumerateSourceIds(messageGroupQuery, + cancellationToken), + IMessageQuery messageQuery => sourceRepository.EnumerateSourceIds(messageQuery, cancellationToken), + ILeaseQuery leaseQuery => sourceRepository.EnumerateSourceIds(leaseQuery, cancellationToken), + ITagQuery tagQuery => sourceRepository.EnumerateSourceIds(tagQuery, cancellationToken), + _ => AsyncEnumerable.Empty(), + }; + } + + /// + /// Reconstruct the object by source id + /// + /// The source repository + /// The source id + /// A cancellation token + /// An instance of . + /// + /// This does *not* initialize any of the following: + ///
    + ///
  • + /// + ///
  • + ///
  • + /// + ///
  • + ///
  • + /// + ///
  • + ///
  • + /// + ///
  • + ///
+ /// They will be empty. + ///
+ public static async Task GetSource + ( + this ISourceRepository sourceRepository, + Id sourceId, + CancellationToken cancellationToken + ) + { + var query = new GetSourceQuery(sourceId); + + var annotatedAgentSignature = await sourceRepository + .EnumerateAnnotatedAgentSignatures(query, cancellationToken) + .SingleAsync(cancellationToken); + + var sourceMessages = await sourceRepository + .EnumerateAnnotatedDeltas(query, cancellationToken) + .Select(annotatedDeltas => new Message + { + EntityPointer = annotatedDeltas.EntityPointer, Delta = annotatedDeltas.Data, + }) + .ToArrayAsync(cancellationToken); + + return new Source + { + Id = annotatedAgentSignature.SourceId, + TimeStamp = annotatedAgentSignature.SourceTimeStamp, + AgentSignature = annotatedAgentSignature.Data, + Messages = sourceMessages.ToImmutableArray(), + }; + } +} diff --git a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs deleted file mode 100644 index b9c88eeb..00000000 --- a/src/EntityDb.Common/Extensions/TransactionRepositoryExtensions.cs +++ /dev/null @@ -1,78 +0,0 @@ -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Queries; -using EntityDb.Common.Transactions; -using System.Collections.Immutable; - -namespace EntityDb.Common.Extensions; - -/// -/// Extensions for . -/// -public static class TransactionRepositoryExtensions -{ - /// - /// Enumerate transaction ids for any supported query type - /// - /// The transaction repository - /// The query - /// A cancellation token - /// Transaction ids - public static IAsyncEnumerable EnumerateTransactionIds(this ITransactionRepository transactionRepository, IQuery query, CancellationToken cancellationToken = default) - { - return query switch - { - IAgentSignatureQuery agentSignatureQuery => transactionRepository.EnumerateTransactionIds(agentSignatureQuery, cancellationToken), - ICommandQuery commandQuery => transactionRepository.EnumerateTransactionIds(commandQuery, cancellationToken), - ILeaseQuery leaseQuery => transactionRepository.EnumerateTransactionIds(leaseQuery, cancellationToken), - ITagQuery tagQuery => transactionRepository.EnumerateTransactionIds(tagQuery, cancellationToken), - _ => AsyncEnumerable.Empty(), - }; - } - - /// - /// Reconstruct the object by transaction id - /// - /// The transaction repository - /// The transaction id - /// A cancellation token - /// An instance of . - /// - /// This does *not* initialize , , - /// , nor . They will be empty. - /// - public static async Task GetTransaction(this ITransactionRepository transactionRepository, Id transactionId, CancellationToken cancellationToken) - { - var query = new GetTransactionCommandsQuery(transactionId); - - var annotatedAgentSignature = await transactionRepository - .EnumerateAnnotatedAgentSignatures(query, cancellationToken) - .SingleAsync(cancellationToken); - - var annotatedCommands = await transactionRepository - .EnumerateAnnotatedCommands(query, cancellationToken) - .Select(annotatedCommand => new TransactionCommand - { - EntityId = annotatedCommand.EntityId, - EntityVersionNumber = annotatedCommand.EntityVersionNumber, - Data = annotatedCommand.Data, - AddLeases = ImmutableArray.Empty, - AddTags = ImmutableArray.Empty, - DeleteLeases = ImmutableArray.Empty, - DeleteTags = ImmutableArray.Empty, - }) - .ToArrayAsync(cancellationToken); - - return new Transaction - { - Id = annotatedAgentSignature.TransactionId, - TimeStamp = annotatedAgentSignature.TransactionTimeStamp, - AgentSignature = annotatedAgentSignature.Data, - Commands = annotatedCommands.ToImmutableArray(), - }; - } -} diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs deleted file mode 100644 index 221ba73d..00000000 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ /dev/null @@ -1,35 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Snapshots; - -namespace EntityDb.Common.Projections; - -/// -/// Provides basic functionality for the common implementation of projections. -/// -/// -public interface IProjection : ISnapshot -{ - /// - /// Incorporates the source into the projection. - /// - /// The source of information - void Mutate(ISource source); - - /// - /// Returns a that finds transactions that need to be passed to the reducer. - /// - /// The source repository, which can be used to locate new information - /// A pointer to the desired projection state - /// A cancellation token - /// A that is used to load the rest of the transaction commands for the given projection pointer. - IAsyncEnumerable EnumerateSources(ISourceRepository sourceRepository, Pointer projectionPointer, CancellationToken cancellationToken); - - /// - /// Maps a source to a projection id, or default if the entity does not map to this projection. - /// - /// The source that could trigger a projection - /// The projection id for the entity, or default if none. - static abstract IEnumerable EnumerateProjectionIds(ISource source); -} diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index c67ccdd4..2bd630d3 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,7 +1,5 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; @@ -9,21 +7,22 @@ namespace EntityDb.Common.Projections; -internal sealed class ProjectionRepository : DisposableResourceBaseClass, IProjectionRepository +internal sealed class ProjectionRepository : DisposableResourceBaseClass, + IProjectionRepository where TProjection : IProjection { + private readonly IServiceProvider _serviceProvider; + public ProjectionRepository ( - ITransactionRepository transactionRepository, + IServiceProvider serviceProvider, ISnapshotRepository? snapshotRepository = null ) { - TransactionRepository = transactionRepository; + _serviceProvider = serviceProvider; SnapshotRepository = snapshotRepository; } - public ITransactionRepository TransactionRepository { get; } - public ISnapshotRepository? SnapshotRepository { get; } public async Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default) @@ -33,14 +32,14 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati TProjection.Construct(projectionPointer.Id) : TProjection.Construct(projectionPointer.Id); - var sources = projection.EnumerateSources(this, projectionPointer, cancellationToken); + var sources = projection.EnumerateSources(_serviceProvider, projectionPointer, cancellationToken); await foreach (var source in sources) { projection.Mutate(source); } - if (!projectionPointer.IsSatisfiedBy(projection.GetVersionNumber())) + if (!projectionPointer.IsSatisfiedBy(projection.GetPointer())) { throw new SnapshotPointerDoesNotExistException(); } @@ -49,16 +48,14 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati } public static IProjectionRepository Create(IServiceProvider serviceProvider, - ITransactionRepository transactionRepository, ISnapshotRepository? snapshotRepository = null) { if (snapshotRepository is null) { - return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionRepository); + return ActivatorUtilities.CreateInstance>(serviceProvider); } return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionRepository, snapshotRepository); + snapshotRepository); } } diff --git a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs index 2db48c92..f15f69fe 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; namespace EntityDb.Common.Projections; @@ -9,36 +8,32 @@ internal class ProjectionRepositoryFactory : IProjectionRepositoryF { private readonly IServiceProvider _serviceProvider; private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; public ProjectionRepositoryFactory ( IServiceProvider serviceProvider, - ITransactionRepositoryFactory transactionRepositoryFactory, ISnapshotRepositoryFactory? snapshotRepositoryFactory = null ) { _serviceProvider = serviceProvider; - _transactionRepositoryFactory = transactionRepositoryFactory; _snapshotRepositoryFactory = snapshotRepositoryFactory; } - public async Task> CreateRepository(string transactionSessionOptionsName, - string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default) + public async Task> CreateRepository + ( + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ) { - var transactionRepository = - await _transactionRepositoryFactory.CreateRepository(transactionSessionOptionsName, cancellationToken); - if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) { - return ProjectionRepository.Create(_serviceProvider, - transactionRepository); + return ProjectionRepository.Create(_serviceProvider); } var snapshotRepository = await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); return ProjectionRepository.Create(_serviceProvider, - transactionRepository, snapshotRepository); + snapshotRepository); } } diff --git a/src/EntityDb.Common/Properties/AssemblyInfo.cs b/src/EntityDb.Common/Properties/AssemblyInfo.cs index 912c98c8..9a83aa60 100644 --- a/src/EntityDb.Common/Properties/AssemblyInfo.cs +++ b/src/EntityDb.Common/Properties/AssemblyInfo.cs @@ -1,15 +1,10 @@ using System.Runtime.CompilerServices; // src -[assembly: InternalsVisibleTo("EntityDb.SqlDb")] -[assembly: InternalsVisibleTo("EntityDb.Npgsql")] -[assembly: InternalsVisibleTo("EntityDb.EntityFramework")] -[assembly: InternalsVisibleTo("EntityDb.InMemory")] [assembly: InternalsVisibleTo("EntityDb.MongoDb")] [assembly: InternalsVisibleTo("EntityDb.Provisioner")] [assembly: InternalsVisibleTo("EntityDb.Mvc")] [assembly: InternalsVisibleTo("EntityDb.Redis")] -[assembly: InternalsVisibleTo("EntityDb.Void")] [assembly: InternalsVisibleTo("EntityDb.Json")] // test diff --git a/src/EntityDb.Common/Queries/DeleteLeasesQuery.cs b/src/EntityDb.Common/Queries/DeleteLeasesQuery.cs deleted file mode 100644 index bec312f5..00000000 --- a/src/EntityDb.Common/Queries/DeleteLeasesQuery.cs +++ /dev/null @@ -1,37 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Queries; - -internal sealed record DeleteLeasesQuery(Id EntityId, IReadOnlyCollection Leases, object? Options = null) : ILeaseQuery -{ - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.And - ( - builder.EntityIdIn(EntityId), - builder.Or - ( - Leases - .Select(deleteLease => builder.And( - builder.LeaseScopeEq(deleteLease.Scope), - builder.LeaseLabelEq(deleteLease.Label), - builder.LeaseValueEq(deleteLease.Value) - )) - .ToArray() - ) - ); - } - - public TSort? GetSort(ILeaseSortBuilder builder) - { - return default; - } - - public int? Skip => null; - - public int? Take => null; -} diff --git a/src/EntityDb.Common/Queries/GetAgentSignatures.cs b/src/EntityDb.Common/Queries/GetAgentSignatures.cs deleted file mode 100644 index 2052b620..00000000 --- a/src/EntityDb.Common/Queries/GetAgentSignatures.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Queries; - -internal sealed record GetAgentSignatures(Id[] TransactionIds) : IAgentSignatureQuery -{ - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) - { - return builder.SourceIdIn(TransactionIds); - } - - public TSort GetSort(IAgentSignatureSortBuilder builder) - { - return builder.SourceTimeStamp(true); - } - - public int? Skip => null; - - public int? Take => null; - - public object? Options => null; -} diff --git a/src/EntityDb.Common/Queries/GetEntityQuery.cs b/src/EntityDb.Common/Queries/GetEntityQuery.cs deleted file mode 100644 index 023cec32..00000000 --- a/src/EntityDb.Common/Queries/GetEntityQuery.cs +++ /dev/null @@ -1,37 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Queries; - -internal sealed record GetEntityCommandsQuery - (Pointer EntityPointer, VersionNumber SnapshotVersionNumber, object? Options = null) : ICommandQuery -{ - public TFilter GetFilter(ICommandFilterBuilder builder) - { - var filters = new List - { - builder.EntityIdIn(EntityPointer.Id), builder.EntityVersionNumberGte(SnapshotVersionNumber.Next()) - }; - - if (EntityPointer.VersionNumber != VersionNumber.MinValue) - { - filters.Add(builder.EntityVersionNumberLte(EntityPointer.VersionNumber)); - } - - return builder.And(filters.ToArray()); - } - - public TSort GetSort(ICommandSortBuilder builder) - { - return builder.Combine - ( - builder.EntityVersionNumber(true) - ); - } - - public int? Skip => null; - - public int? Take => null; -} diff --git a/src/EntityDb.Common/Queries/GetLastEntityVersionQuery.cs b/src/EntityDb.Common/Queries/GetLastEntityVersionQuery.cs deleted file mode 100644 index 2eda63d3..00000000 --- a/src/EntityDb.Common/Queries/GetLastEntityVersionQuery.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Queries; - -internal sealed record GetLastEntityCommandQuery(Id EntityId, object? Options = null) : ICommandQuery -{ - public TFilter GetFilter(ICommandFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(ICommandSortBuilder builder) - { - return builder.EntityVersionNumber(false); - } - - public int? Skip => null; - - public int? Take => 1; -} diff --git a/src/EntityDb.Common/Queries/GetTransactionQuery.cs b/src/EntityDb.Common/Queries/GetTransactionQuery.cs deleted file mode 100644 index 3b1c551e..00000000 --- a/src/EntityDb.Common/Queries/GetTransactionQuery.cs +++ /dev/null @@ -1,35 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Queries; - -internal sealed record GetTransactionCommandsQuery(Id TransactionId) : IAgentSignatureQuery, ICommandQuery -{ - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) - { - return builder.SourceIdIn(TransactionId); - } - - public TSort? GetSort(IAgentSignatureSortBuilder builder) - { - return default; - } - - public TFilter GetFilter(ICommandFilterBuilder builder) - { - return builder.SourceIdIn(TransactionId); - } - - public TSort? GetSort(ICommandSortBuilder builder) - { - return default; - } - - public int? Skip => default; - - public int? Take => default; - - public object? Options => default; -} diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedAgentSignatureQuery.cs b/src/EntityDb.Common/Queries/Modified/ModifiedAgentSignatureQuery.cs deleted file mode 100644 index 2f6868a1..00000000 --- a/src/EntityDb.Common/Queries/Modified/ModifiedAgentSignatureQuery.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Common.Extensions; - -namespace EntityDb.Common.Queries.Modified; - -internal sealed record ModifiedAgentSignatureQuery - (IAgentSignatureQuery AgentSignatureQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase( - AgentSignatureQuery, - ModifiedQueryOptions), IAgentSignatureQuery -{ - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) - { - if (ModifiedQueryOptions.InvertFilter) - { - return builder.Not - ( - AgentSignatureQuery.GetFilter(builder) - ); - } - - return AgentSignatureQuery.GetFilter(builder); - } - - public TSort? GetSort(IAgentSignatureSortBuilder builder) - { - return AgentSignatureQuery.GetSort(ModifiedQueryOptions.ReverseSort - ? builder.Reverse() - : builder); - } -} diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedCommandQuery.cs b/src/EntityDb.Common/Queries/Modified/ModifiedCommandQuery.cs deleted file mode 100644 index 9379e5d3..00000000 --- a/src/EntityDb.Common/Queries/Modified/ModifiedCommandQuery.cs +++ /dev/null @@ -1,31 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Common.Extensions; - -namespace EntityDb.Common.Queries.Modified; - -internal sealed record ModifiedCommandQuery - (ICommandQuery CommandQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(CommandQuery, - ModifiedQueryOptions), ICommandQuery -{ - public TFilter GetFilter(ICommandFilterBuilder builder) - { - if (ModifiedQueryOptions.InvertFilter) - { - return builder.Not - ( - CommandQuery.GetFilter(builder) - ); - } - - return CommandQuery.GetFilter(builder); - } - - public TSort? GetSort(ICommandSortBuilder builder) - { - return CommandQuery.GetSort(ModifiedQueryOptions.ReverseSort - ? builder.Reverse() - : builder); - } -} diff --git a/src/EntityDb.Common/Queries/SortBuilders/AgentSignatureReverseSortBuilder.cs b/src/EntityDb.Common/Queries/SortBuilders/AgentSignatureReverseSortBuilder.cs deleted file mode 100644 index 3e38c85b..00000000 --- a/src/EntityDb.Common/Queries/SortBuilders/AgentSignatureReverseSortBuilder.cs +++ /dev/null @@ -1,19 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; - -namespace EntityDb.Common.Queries.SortBuilders; - -internal sealed record AgentSignatureReverseSortBuilder - (IAgentSignatureSortBuilder AgentSignatureSortBuilder) : ReverseSortBuilderBase( - AgentSignatureSortBuilder), - IAgentSignatureSortBuilder -{ - public TSort EntityIds(bool ascending) - { - return AgentSignatureSortBuilder.EntityIds(!ascending); - } - - public TSort AgentSignatureType(bool ascending) - { - return AgentSignatureSortBuilder.AgentSignatureType(!ascending); - } -} diff --git a/src/EntityDb.Common/Queries/SortBuilders/CommandReverseSortBuilder.cs b/src/EntityDb.Common/Queries/SortBuilders/CommandReverseSortBuilder.cs deleted file mode 100644 index 9259e436..00000000 --- a/src/EntityDb.Common/Queries/SortBuilders/CommandReverseSortBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; - -namespace EntityDb.Common.Queries.SortBuilders; - -internal sealed record CommandReverseSortBuilder - (ICommandSortBuilder CommandSortBuilder) : ReverseSortBuilderBase(CommandSortBuilder), - ICommandSortBuilder -{ - public TSort EntityId(bool ascending) - { - return CommandSortBuilder.EntityId(!ascending); - } - - public TSort EntityVersionNumber(bool ascending) - { - return CommandSortBuilder.EntityVersionNumber(!ascending); - } - - public TSort CommandType(bool ascending) - { - return CommandSortBuilder.CommandType(!ascending); - } -} diff --git a/src/EntityDb.Common/Agents/AgentAccessorChain.cs b/src/EntityDb.Common/Sources/Agents/AgentAccessorChain.cs similarity index 87% rename from src/EntityDb.Common/Agents/AgentAccessorChain.cs rename to src/EntityDb.Common/Sources/Agents/AgentAccessorChain.cs index b7f05f3b..ff91ac3e 100644 --- a/src/EntityDb.Common/Agents/AgentAccessorChain.cs +++ b/src/EntityDb.Common/Sources/Agents/AgentAccessorChain.cs @@ -1,10 +1,10 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace EntityDb.Common.Agents; +namespace EntityDb.Common.Sources.Agents; /// /// Represents a type that chains together multiple instances of and returns the @@ -43,13 +43,13 @@ IServiceProvider outerServiceProvider } /// - public async Task GetAgentAsync(string signatureOptionsName, CancellationToken cancellationToken) + public async Task GetAgent(string signatureOptionsName, CancellationToken cancellationToken) { foreach (var agentAccessor in _agentAccessors) { try { - return await agentAccessor.GetAgentAsync(signatureOptionsName, cancellationToken); + return await agentAccessor.GetAgent(signatureOptionsName, cancellationToken); } catch (Exception exception) { diff --git a/src/EntityDb.Common/Agents/AgentAccessorChainOptions.cs b/src/EntityDb.Common/Sources/Agents/AgentAccessorChainOptions.cs similarity index 91% rename from src/EntityDb.Common/Agents/AgentAccessorChainOptions.cs rename to src/EntityDb.Common/Sources/Agents/AgentAccessorChainOptions.cs index b63bd145..7afb9fb3 100644 --- a/src/EntityDb.Common/Agents/AgentAccessorChainOptions.cs +++ b/src/EntityDb.Common/Sources/Agents/AgentAccessorChainOptions.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; using Microsoft.Extensions.DependencyInjection; -namespace EntityDb.Common.Agents; +namespace EntityDb.Common.Sources.Agents; /// /// Options for configuring the . diff --git a/src/EntityDb.Common/Agents/StandardAgent.cs b/src/EntityDb.Common/Sources/Agents/StandardAgent.cs similarity index 64% rename from src/EntityDb.Common/Agents/StandardAgent.cs rename to src/EntityDb.Common/Sources/Agents/StandardAgent.cs index 989639aa..b5be9b49 100644 --- a/src/EntityDb.Common/Agents/StandardAgent.cs +++ b/src/EntityDb.Common/Sources/Agents/StandardAgent.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Common.Agents; +namespace EntityDb.Common.Sources.Agents; internal sealed record StandardAgent(object Signature) : IAgent { diff --git a/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs b/src/EntityDb.Common/Sources/Agents/UnknownAgentAccessor.cs similarity index 75% rename from src/EntityDb.Common/Agents/UnknownAgentAccessor.cs rename to src/EntityDb.Common/Sources/Agents/UnknownAgentAccessor.cs index 82278f71..d492746f 100644 --- a/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs +++ b/src/EntityDb.Common/Sources/Agents/UnknownAgentAccessor.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; -namespace EntityDb.Common.Agents; +namespace EntityDb.Common.Sources.Agents; /// /// Represents a type that indicates there is no known actor. @@ -18,13 +18,13 @@ public UnknownAgentAccessor(IAgentSignatureAugmenter? agentSignatureAugmenter = } /// - public async Task GetAgentAsync(string signatureOptionsName, CancellationToken cancellationToken) + public async Task GetAgent(string signatureOptionsName, CancellationToken cancellationToken) { var applicationInfo = DefaultApplicationInfo; if (_agentSignatureAugmenter != null) { - applicationInfo = await _agentSignatureAugmenter.GetApplicationInfoAsync(cancellationToken); + applicationInfo = await _agentSignatureAugmenter.GetApplicationInfo(cancellationToken); } return new StandardAgent(new UnknownAgentSignature(applicationInfo)); diff --git a/src/EntityDb.Common/Agents/UnknownAgentSignature.cs b/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs similarity index 76% rename from src/EntityDb.Common/Agents/UnknownAgentSignature.cs rename to src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs index b10b02a4..cbd7e719 100644 --- a/src/EntityDb.Common/Agents/UnknownAgentSignature.cs +++ b/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Common.Agents; +namespace EntityDb.Common.Sources.Agents; /// /// Represents the signature of an unknown actor. diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs new file mode 100644 index 00000000..2aaa8af2 --- /dev/null +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs @@ -0,0 +1,33 @@ +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Annotations; + +internal record AnnotatedSourceData +( + Id SourceId, + TimeStamp SourceTimeStamp, + TData Data, + Pointer EntityPointer +) : IAnnotatedSourceData +{ + public static IAnnotatedSourceData CreateFromBoxedData + ( + Id sourceId, + TimeStamp sourceTimeStamp, + object boxedData, + Pointer entityPointer + ) + { + var dataAnnotationType = typeof(AnnotatedSourceData<>).MakeGenericType(boxedData.GetType()); + + return (IAnnotatedSourceData)Activator.CreateInstance + ( + dataAnnotationType, + sourceId, + sourceTimeStamp, + boxedData, + entityPointer + )!; + } +} diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs new file mode 100644 index 00000000..0a0e393b --- /dev/null +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs @@ -0,0 +1,33 @@ +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Annotations; + +internal record AnnotatedSourceGroupData +( + Id SourceId, + TimeStamp SourceTimeStamp, + TData Data, + Pointer[] EntityPointers +) : IAnnotatedSourceGroupData +{ + public static IAnnotatedSourceGroupData CreateFromBoxedData + ( + Id sourceId, + TimeStamp sourceTimeStamp, + object boxedData, + Pointer[] entityPointers + ) + { + var dataAnnotationType = typeof(AnnotatedSourceGroupData<>).MakeGenericType(boxedData.GetType()); + + return (IAnnotatedSourceGroupData)Activator.CreateInstance + ( + dataAnnotationType, + sourceId, + sourceTimeStamp, + boxedData, + entityPointers + )!; + } +} diff --git a/src/EntityDb.Common/Leases/Lease.cs b/src/EntityDb.Common/Sources/Attributes/Lease.cs similarity index 52% rename from src/EntityDb.Common/Leases/Lease.cs rename to src/EntityDb.Common/Sources/Attributes/Lease.cs index b0544e98..9b55cda4 100644 --- a/src/EntityDb.Common/Leases/Lease.cs +++ b/src/EntityDb.Common/Sources/Attributes/Lease.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Sources.Attributes; -namespace EntityDb.Common.Leases; +namespace EntityDb.Common.Sources.Attributes; /// public sealed record Lease(string Scope, string Label, string Value) : ILease; diff --git a/src/EntityDb.Common/Sources/Attributes/Tag.cs b/src/EntityDb.Common/Sources/Attributes/Tag.cs new file mode 100644 index 00000000..ab0905bf --- /dev/null +++ b/src/EntityDb.Common/Sources/Attributes/Tag.cs @@ -0,0 +1,6 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Common.Sources.Attributes; + +/// +public sealed record Tag(string Label, string Value) : ITag; diff --git a/src/EntityDb.Common/Extensions/DocumentsExtensions.cs b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs similarity index 54% rename from src/EntityDb.Common/Extensions/DocumentsExtensions.cs rename to src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs index 9e71994e..7275eb7f 100644 --- a/src/EntityDb.Common/Extensions/DocumentsExtensions.cs +++ b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs @@ -1,11 +1,10 @@ -using EntityDb.Abstractions.Annotations; +using EntityDb.Abstractions.Sources.Annotations; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Annotations; -using EntityDb.Common.Documents; using EntityDb.Common.Envelopes; +using EntityDb.Common.Sources.Annotations; using System.Runtime.CompilerServices; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources.Documents; internal static class DocumentsExtensions { @@ -34,13 +33,39 @@ Func, IAsyncEnumerable> mapToIds return ids; } - public static async IAsyncEnumerable> EnumerateEntityAnnotation + public static IAsyncEnumerable EnumeratePointers + ( + this IAsyncEnumerable documents, + int? skip, + int? limit, + Func, IAsyncEnumerable> mapToPointers + ) + { + var pointers = mapToPointers + .Invoke(documents) + .Distinct(); + + if (skip.HasValue) + { + pointers = pointers.Skip(skip.Value); + } + + if (limit.HasValue) + { + pointers = pointers.Take(limit.Value); + } + + return pointers; + } + + public static async IAsyncEnumerable> EnumerateEntityAnnotation ( this IAsyncEnumerable documents, IEnvelopeService envelopeService, [EnumeratorCancellation] CancellationToken cancellationToken ) - where TDocument : IEntityDocument + where TDocument : IMessageDocument where TData : notnull { await using var enumerator = documents.GetAsyncEnumerator(cancellationToken); @@ -49,24 +74,24 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var document = enumerator.Current; - yield return EntityAnnotation.CreateFromBoxedData + yield return AnnotatedSourceData.CreateFromBoxedData ( - document.TransactionId, - document.TransactionTimeStamp, - document.EntityId, - document.EntityVersionNumber, - envelopeService.Deserialize(document.Data) + document.SourceId, + document.SourceTimeStamp, + envelopeService.Deserialize(document.Data), + document.EntityPointer ); } } - public static async IAsyncEnumerable> EnumerateEntitiesAnnotation + public static async IAsyncEnumerable> EnumerateEntitiesAnnotation ( this IAsyncEnumerable documents, IEnvelopeService envelopeService, [EnumeratorCancellation] CancellationToken cancellationToken ) - where TDocument : IEntitiesDocument + where TDocument : IMessageGroupDocument where TData : notnull { await using var enumerator = documents.GetAsyncEnumerator(cancellationToken); @@ -75,12 +100,12 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var document = enumerator.Current; - yield return EntitiesAnnotation.CreateFromBoxedData + yield return AnnotatedSourceGroupData.CreateFromBoxedData ( - document.TransactionId, - document.TransactionTimeStamp, - document.EntityIds, - envelopeService.Deserialize(document.Data) + document.SourceId, + document.SourceTimeStamp, + envelopeService.Deserialize(document.Data), + document.EntityPointers ); } } diff --git a/src/EntityDb.Common/Sources/Documents/IDocument.cs b/src/EntityDb.Common/Sources/Documents/IDocument.cs new file mode 100644 index 00000000..dee5cc52 --- /dev/null +++ b/src/EntityDb.Common/Sources/Documents/IDocument.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Documents; + +internal interface IDocument +{ + Id SourceId { get; } + TimeStamp SourceTimeStamp { get; } + TData Data { get; } +} diff --git a/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs b/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs new file mode 100644 index 00000000..5c68f2b5 --- /dev/null +++ b/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs @@ -0,0 +1,8 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Documents; + +internal interface IMessageDocument : IDocument +{ + Pointer EntityPointer { get; } +} diff --git a/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs b/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs new file mode 100644 index 00000000..c3acc752 --- /dev/null +++ b/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs @@ -0,0 +1,8 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Documents; + +internal interface IMessageGroupDocument : IDocument +{ + Pointer[] EntityPointers { get; } +} diff --git a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs index 281069cb..2b21eab3 100644 --- a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs @@ -1,9 +1,6 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; -using EntityDb.Common.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -16,36 +13,36 @@ namespace EntityDb.Common.Sources.Processors; public sealed class EntitySnapshotSourceProcessor : ISourceProcessor where TEntity : IEntity { - private readonly ILogger> _logger; private readonly IEntityRepositoryFactory _entityRepositoryFactory; + private readonly ILogger> _logger; private readonly string _snapshotSessionOptionsName; - private readonly string _transactionSessionOptionsName; + private readonly string _sourceSessionOptionsName; /// public EntitySnapshotSourceProcessor ( ILogger> logger, IEntityRepositoryFactory entityRepositoryFactory, - string transactionSessionOptionsName, + string sourceSessionOptionsName, string snapshotSessionOptionsName ) { _logger = logger; _entityRepositoryFactory = entityRepositoryFactory; - _transactionSessionOptionsName = transactionSessionOptionsName; + _sourceSessionOptionsName = sourceSessionOptionsName; _snapshotSessionOptionsName = snapshotSessionOptionsName; } /// - public async Task Process(ISource source, CancellationToken cancellationToken) + public async Task Process(Source source, CancellationToken cancellationToken) { - if (source is not ITransaction transaction) + if (!source.Messages.Any(message => TEntity.CanReduce(message.Delta))) { throw new NotSupportedException(); } await using var entityRepository = await _entityRepositoryFactory - .CreateRepository(_transactionSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); + .CreateRepository(_sourceSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); if (entityRepository.SnapshotRepository is null) { @@ -57,21 +54,22 @@ public async Task Process(ISource source, CancellationToken cancellationToken) var latestEntities = new Dictionary(); var saveEntities = new Dictionary(); - foreach (var command in transaction.Commands) - { - var entityId = command.EntityId; + foreach (var message in source.Messages) + { + var entityPointer = message.EntityPointer; - if (!latestEntities.Remove(entityId, out var previousEntity)) + if (!latestEntities.Remove(entityPointer.Id, out var previousEntity)) { - previousEntity = await entityRepository.SnapshotRepository.GetSnapshotOrDefault(entityId, cancellationToken); + previousEntity = + await entityRepository.SnapshotRepository.GetSnapshotOrDefault(entityPointer, cancellationToken); } - var nextEntity = (previousEntity ?? TEntity.Construct(entityId)).Reduce(command.Data); + var nextEntity = (previousEntity ?? TEntity.Construct(entityPointer.Id)).Reduce(message.Delta); var nextEntityPointer = nextEntity.GetPointer(); if (nextEntity.ShouldRecordAsLatest(previousEntity)) { - saveEntities[entityId] = nextEntity; + saveEntities[entityPointer.Id] = nextEntity; } if (nextEntity.ShouldRecord()) @@ -79,7 +77,7 @@ public async Task Process(ISource source, CancellationToken cancellationToken) saveEntities[nextEntityPointer] = nextEntity; } - latestEntities[entityId] = nextEntity; + latestEntities[entityPointer.Id] = nextEntity; cancellationToken.ThrowIfCancellationRequested(); } @@ -91,9 +89,9 @@ public async Task Process(ISource source, CancellationToken cancellationToken) } internal static EntitySnapshotSourceProcessor Create(IServiceProvider serviceProvider, - string transactionSessionOptionsName, string snapshotSessionOptionsName) + string sourceSessionOptionsName, string snapshotSessionOptionsName) { return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionSessionOptionsName, snapshotSessionOptionsName); + sourceSessionOptionsName, snapshotSessionOptionsName); } } diff --git a/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs index 52109f42..7ed1cb76 100644 --- a/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Sources.Processors; /// -/// Represents a type that processes sources emitted to a . +/// Represents a type that processes sources emitted to a . /// public interface ISourceProcessor { @@ -13,5 +13,5 @@ public interface ISourceProcessor /// The source that has been received. /// A cancellation token. /// A task - Task Process(ISource source, CancellationToken cancellationToken); + Task Process(Source source, CancellationToken cancellationToken); } diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs index d0b19a83..218cf3cc 100644 --- a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs @@ -1,7 +1,5 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; -using EntityDb.Common.Extensions; -using EntityDb.Common.Projections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -17,28 +15,25 @@ public sealed class ProjectionSnapshotSourceProcessor : ISourceProc private readonly ILogger> _logger; private readonly IProjectionRepositoryFactory _projectionRepositoryFactory; private readonly string _snapshotSessionOptionsName; - private readonly string _transactionSessionOptionsName; /// public ProjectionSnapshotSourceProcessor ( ILogger> logger, IProjectionRepositoryFactory projectionRepositoryFactory, - string transactionSessionOptionsName, string snapshotSessionOptionsName ) { _logger = logger; _projectionRepositoryFactory = projectionRepositoryFactory; - _transactionSessionOptionsName = transactionSessionOptionsName; _snapshotSessionOptionsName = snapshotSessionOptionsName; } /// - public async Task Process(ISource source, CancellationToken cancellationToken) + public async Task Process(Source source, CancellationToken cancellationToken) { await using var projectionRepository = await _projectionRepositoryFactory - .CreateRepository(_transactionSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); + .CreateRepository(_snapshotSessionOptionsName, cancellationToken); if (projectionRepository.SnapshotRepository is null) { @@ -47,13 +42,13 @@ public async Task Process(ISource source, CancellationToken cancellationToken) return; } - foreach (var projectionId in TProjection.EnumerateProjectionIds(source)) + foreach (var entityId in TProjection.EnumerateEntityIds(source)) { var previousProjection = await projectionRepository.SnapshotRepository - .GetSnapshotOrDefault(projectionId, cancellationToken); + .GetSnapshotOrDefault(entityId, cancellationToken); var nextProjection = previousProjection == null - ? TProjection.Construct(projectionId) + ? TProjection.Construct(entityId) : previousProjection; nextProjection.Mutate(source); @@ -62,12 +57,14 @@ public async Task Process(ISource source, CancellationToken cancellationToken) if (nextProjection.ShouldRecordAsLatest(previousProjection)) { - await projectionRepository.SnapshotRepository.PutSnapshot(projectionId, nextProjection, cancellationToken); + await projectionRepository.SnapshotRepository.PutSnapshot(entityId, nextProjection, + cancellationToken); } if (nextProjection.ShouldRecord()) { - await projectionRepository.SnapshotRepository.PutSnapshot(nextProjectionPointer, nextProjection, cancellationToken); + await projectionRepository.SnapshotRepository.PutSnapshot(nextProjectionPointer, nextProjection, + cancellationToken); } cancellationToken.ThrowIfCancellationRequested(); @@ -75,10 +72,9 @@ public async Task Process(ISource source, CancellationToken cancellationToken) } internal static ProjectionSnapshotSourceProcessor Create(IServiceProvider serviceProvider, - string transactionSessionOptionsName, string snapshotSessionOptionsName) + string snapshotSessionOptionsName) { return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionSessionOptionsName, snapshotSessionOptionsName); } } diff --git a/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs index 82530eac..291afc21 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs @@ -13,7 +13,8 @@ internal class BufferBlockSourceProcessorQueue : BackgroundService, ISourceProce private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; - public BufferBlockSourceProcessorQueue(ILogger logger, IServiceScopeFactory serviceScopeFactory) + public BufferBlockSourceProcessorQueue(ILogger logger, + IServiceScopeFactory serviceScopeFactory) { _logger = logger; _serviceScopeFactory = serviceScopeFactory; @@ -34,13 +35,13 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) using var logScope = _logger.BeginScope(new KeyValuePair[] { - new("SourceProcessorType", item.SourceProcessorType.Name), - new("SourceId", item.Source.Id.Value) + new("SourceProcessorType", item.SourceProcessorType.Name), new("SourceId", item.Source.Id.Value), }); try { - var sourceProcessor = (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(item.SourceProcessorType); + var sourceProcessor = + (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(item.SourceProcessorType); _logger.LogDebug("Started processing source"); diff --git a/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs b/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs index 0693dad7..b1bde5f9 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/ISourceProcessorQueueItem.cs @@ -3,18 +3,18 @@ namespace EntityDb.Common.Sources.Processors.Queues; /// -/// An item of work for a +/// An item of work for a /// public interface ISourceProcessorQueueItem { /// /// The type of the source processor, which *must* implement - /// . + /// . /// Type SourceProcessorType { get; } /// /// The source to be processed. /// - ISource Source { get; } + Source Source { get; } } diff --git a/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs index 828b781b..3cf2c047 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs @@ -5,5 +5,5 @@ namespace EntityDb.Common.Sources.Processors.Queues; internal class SourceProcessorQueueItem : ISourceProcessorQueueItem { public required Type SourceProcessorType { get; init; } - public required ISource Source { get; init; } + public required Source Source { get; init; } } diff --git a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs index c14897fd..5114eb58 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs @@ -8,25 +8,31 @@ internal class TestModeSourceProcessorQueue : ISourceProcessorQueue private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; - public TestModeSourceProcessorQueue(ILogger logger, IServiceScopeFactory serviceScopeFactory) + public TestModeSourceProcessorQueue(ILogger logger, + IServiceScopeFactory serviceScopeFactory) { _logger = logger; _serviceScopeFactory = serviceScopeFactory; } + public void Enqueue(ISourceProcessorQueueItem item) + { + Task.Run(() => Process(item, default)).Wait(); + } + private async Task Process(ISourceProcessorQueueItem item, CancellationToken cancellationToken) { await using var serviceScope = _serviceScopeFactory.CreateAsyncScope(); using var logScope = _logger.BeginScope(new KeyValuePair[] { - new("SourceProcessorType", item.SourceProcessorType.Name), - new("SourceId", item.Source.Id.Value), + new("SourceProcessorType", item.SourceProcessorType.Name), new("SourceId", item.Source.Id.Value), }); try { - var sourceProcessor = (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(item.SourceProcessorType); + var sourceProcessor = + (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(item.SourceProcessorType); _logger.LogDebug("Started processing source"); @@ -39,9 +45,4 @@ private async Task Process(ISourceProcessorQueueItem item, CancellationToken can _logger.LogError(exception, "Error occurred while processing source"); } } - - public void Enqueue(ISourceProcessorQueueItem item) - { - Task.Run(() => Process(item, default)).Wait(); - } } diff --git a/src/EntityDb.Common/Extensions/FilterBuilderExtensions.cs b/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs similarity index 91% rename from src/EntityDb.Common/Extensions/FilterBuilderExtensions.cs rename to src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs index 05d9c287..f8193fba 100644 --- a/src/EntityDb.Common/Extensions/FilterBuilderExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources.Queries.FilterBuilders; /// /// Extensions for filter builders. @@ -17,7 +17,8 @@ public static class FilterBuilderExtensions /// A that excludes objects which do match all filters in /// . /// - public static TFilter Nand(this IFilterBuilder filterBuilder, params TFilter[] filters) + public static TFilter Nand(this IFilterBuilder filterBuilder, + params TFilter[] filters) { return filterBuilder.Not ( @@ -54,7 +55,8 @@ public static TFilter Nor(this IFilterBuilder filterBuilder, p /// A that only includes objects which only match or /// only match . /// - public static TFilter Xor(this IFilterBuilder filterBuilder, TFilter filterA, TFilter filterB) + public static TFilter Xor(this IFilterBuilder filterBuilder, TFilter filterA, + TFilter filterB) { return filterBuilder.Or ( diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedLeaseQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs similarity index 74% rename from src/EntityDb.Common/Queries/Modified/ModifiedLeaseQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs index 45f76c10..068ef193 100644 --- a/src/EntityDb.Common/Queries/Modified/ModifiedLeaseQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Common.Extensions; -namespace EntityDb.Common.Queries.Modified; +namespace EntityDb.Common.Sources.Queries.Modified; internal sealed record ModifiedLeaseQuery (ILeaseQuery LeaseQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(LeaseQuery, diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs new file mode 100644 index 00000000..8ce1a7f5 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Common.Extensions; + +namespace EntityDb.Common.Sources.Queries.Modified; + +internal sealed record ModifiedMessageGroupQuery + (IMessageGroupQuery MessageGroupQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase( + MessageGroupQuery, + ModifiedQueryOptions), IMessageGroupQuery +{ + public TFilter GetFilter(IMessageGroupFilterBuilder builder) + { + if (ModifiedQueryOptions.InvertFilter) + { + return builder.Not + ( + MessageGroupQuery.GetFilter(builder) + ); + } + + return MessageGroupQuery.GetFilter(builder); + } + + public TSort? GetSort(IMessageGroupSortBuilder builder) + { + return MessageGroupQuery.GetSort(ModifiedQueryOptions.ReverseSort + ? builder.Reverse() + : builder); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs new file mode 100644 index 00000000..55323563 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs @@ -0,0 +1,31 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Common.Extensions; + +namespace EntityDb.Common.Sources.Queries.Modified; + +internal sealed record ModifiedMessageQuery + (IMessageQuery MessageQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(MessageQuery, + ModifiedQueryOptions), IMessageQuery +{ + public TFilter GetFilter(IMessageFilterBuilder builder) + { + if (ModifiedQueryOptions.InvertFilter) + { + return builder.Not + ( + MessageQuery.GetFilter(builder) + ); + } + + return MessageQuery.GetFilter(builder); + } + + public TSort? GetSort(IMessageSortBuilder builder) + { + return MessageQuery.GetSort(ModifiedQueryOptions.ReverseSort + ? builder.Reverse() + : builder); + } +} diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedQueryBase.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs similarity index 72% rename from src/EntityDb.Common/Queries/Modified/ModifiedQueryBase.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs index 21e7c06f..6ffc46f7 100644 --- a/src/EntityDb.Common/Queries/Modified/ModifiedQueryBase.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Sources.Queries; -namespace EntityDb.Common.Queries.Modified; +namespace EntityDb.Common.Sources.Queries.Modified; internal abstract record ModifiedQueryBase(IQuery Query, ModifiedQueryOptions ModifiedQueryOptions) { diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedQueryOptions.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs similarity index 83% rename from src/EntityDb.Common/Queries/Modified/ModifiedQueryOptions.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs index 0062d92d..2da67dfe 100644 --- a/src/EntityDb.Common/Queries/Modified/ModifiedQueryOptions.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs @@ -1,8 +1,7 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Common.Extensions; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -namespace EntityDb.Common.Queries.Modified; +namespace EntityDb.Common.Sources.Queries.Modified; /// /// Options for modified queries, which can be created via . @@ -10,7 +9,8 @@ namespace EntityDb.Common.Queries.Modified; public record ModifiedQueryOptions { /// - /// If true, then the new query will return the value of + /// If true, then the new query will return the value of + /// /// applied to the filter of the original query. Otherwise, the new query will return the same filter as the original /// query. /// diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedTagQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs similarity index 74% rename from src/EntityDb.Common/Queries/Modified/ModifiedTagQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs index 525eed2f..6d5e5434 100644 --- a/src/EntityDb.Common/Queries/Modified/ModifiedTagQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Common.Extensions; -namespace EntityDb.Common.Queries.Modified; +namespace EntityDb.Common.Sources.Queries.Modified; internal sealed record ModifiedTagQuery (ITagQuery TagQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(TagQuery, diff --git a/src/EntityDb.Common/Extensions/QueryExtensions.cs b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs similarity index 62% rename from src/EntityDb.Common/Extensions/QueryExtensions.cs rename to src/EntityDb.Common/Sources/Queries/QueryExtensions.cs index 4c163ef7..64702db0 100644 --- a/src/EntityDb.Common/Extensions/QueryExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Common.Queries.Modified; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Common.Sources.Queries.Modified; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources.Queries; /// /// Extensions for queries. @@ -9,29 +9,28 @@ namespace EntityDb.Common.Extensions; public static class QueryExtensions { /// - /// Returns a new, modified . The way in which it is modified depends on the - /// parameters of - /// this extension method. + /// Returns a new, modified . The way in which + /// it is modified depends on the parameters of this extension method. /// - /// The agentSignature query. + /// The message group query. /// The options for modifying the query. - /// A new, modified . - public static IAgentSignatureQuery Modify(this IAgentSignatureQuery agentSignatureQuery, + /// A new, modified . + public static IMessageGroupQuery Modify(this IMessageGroupQuery messageGroupQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedAgentSignatureQuery(agentSignatureQuery, modifiedQueryOptions); + return new ModifiedMessageGroupQuery(messageGroupQuery, modifiedQueryOptions); } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// Returns a new, modified . The way in which it is modified depends on the parameters of /// this extension method. /// - /// The command query. + /// The message query. /// The options for modifying the query. - /// A new, modified . - public static ICommandQuery Modify(this ICommandQuery commandQuery, ModifiedQueryOptions modifiedQueryOptions) + /// A new, modified . + public static IMessageQuery Modify(this IMessageQuery messageQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedCommandQuery(commandQuery, modifiedQueryOptions); + return new ModifiedMessageQuery(messageQuery, modifiedQueryOptions); } /// diff --git a/src/EntityDb.Common/Queries/SortBuilders/LeaseReverseSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs similarity index 58% rename from src/EntityDb.Common/Queries/SortBuilders/LeaseReverseSortBuilder.cs rename to src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs index 51cfc169..04adedd8 100644 --- a/src/EntityDb.Common/Queries/SortBuilders/LeaseReverseSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs @@ -1,8 +1,8 @@ -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Common.Queries.SortBuilders; +namespace EntityDb.Common.Sources.Queries.SortBuilders; -internal sealed record LeaseReverseSortBuilder +internal sealed record ReverseLeaseSortBuilder (ILeaseSortBuilder LeaseSortBuilder) : ReverseSortBuilderBase(LeaseSortBuilder), ILeaseSortBuilder { @@ -11,14 +11,9 @@ public TSort EntityId(bool ascending) return LeaseSortBuilder.EntityId(!ascending); } - public TSort EntityVersionNumber(bool ascending) + public TSort EntityVersion(bool ascending) { - return LeaseSortBuilder.EntityVersionNumber(!ascending); - } - - public TSort LeaseType(bool ascending) - { - return LeaseSortBuilder.LeaseType(!ascending); + return LeaseSortBuilder.EntityVersion(!ascending); } public TSort LeaseScope(bool ascending) diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs new file mode 100644 index 00000000..1bdf935c --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs @@ -0,0 +1,14 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal sealed record ReverseMessageGroupSortBuilder + (IMessageGroupSortBuilder MessageGroupSortBuilder) : ReverseSortBuilderBase( + MessageGroupSortBuilder), + IMessageGroupSortBuilder +{ + public TSort EntityIds(bool ascending) + { + return MessageGroupSortBuilder.EntityIds(!ascending); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs new file mode 100644 index 00000000..6fa9b277 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal sealed record ReverseMessageSortBuilder + (IMessageSortBuilder MessageSortBuilder) : ReverseSortBuilderBase(MessageSortBuilder), + IMessageSortBuilder +{ + public TSort EntityId(bool ascending) + { + return MessageSortBuilder.EntityId(!ascending); + } + + public TSort EntityVersion(bool ascending) + { + return MessageSortBuilder.EntityVersion(!ascending); + } +} diff --git a/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs similarity index 62% rename from src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs rename to src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs index 528802cc..e7fe29d9 100644 --- a/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Common.Queries.SortBuilders; +namespace EntityDb.Common.Sources.Queries.SortBuilders; internal abstract record ReverseSortBuilderBase(ISortBuilder SortBuilder) { @@ -14,6 +14,11 @@ public TSort SourceId(bool ascending) return SortBuilder.SourceId(!ascending); } + public TSort DataType(bool ascending) + { + return SortBuilder.DataType(!ascending); + } + public TSort Combine(params TSort[] sorts) { return SortBuilder.Combine(sorts); diff --git a/src/EntityDb.Common/Queries/SortBuilders/TagReverseSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs similarity index 53% rename from src/EntityDb.Common/Queries/SortBuilders/TagReverseSortBuilder.cs rename to src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs index 68243616..e240ad9f 100644 --- a/src/EntityDb.Common/Queries/SortBuilders/TagReverseSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs @@ -1,8 +1,8 @@ -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; -namespace EntityDb.Common.Queries.SortBuilders; +namespace EntityDb.Common.Sources.Queries.SortBuilders; -internal sealed record TagReverseSortBuilder +internal sealed record ReverseTagSortBuilder (ITagSortBuilder TagSortBuilder) : ReverseSortBuilderBase(TagSortBuilder), ITagSortBuilder { public TSort EntityId(bool ascending) @@ -10,14 +10,9 @@ public TSort EntityId(bool ascending) return TagSortBuilder.EntityId(!ascending); } - public TSort EntityVersionNumber(bool ascending) + public TSort EntityVersion(bool ascending) { - return TagSortBuilder.EntityVersionNumber(!ascending); - } - - public TSort TagType(bool ascending) - { - return TagSortBuilder.TagType(!ascending); + return TagSortBuilder.EntityVersion(!ascending); } public TSort TagLabel(bool ascending) diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs new file mode 100644 index 00000000..7cd5aba2 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs @@ -0,0 +1,34 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record DeleteLeasesQuery(IReadOnlyCollection Leases, + object? Options = null) : ILeaseQuery +{ + public TFilter GetFilter(ILeaseFilterBuilder builder) + { + return builder.Or + ( + Leases + .Select(lease => builder.And + ( + builder.LeaseScopeEq(lease.Scope), + builder.LeaseLabelEq(lease.Label), + builder.LeaseValueEq(lease.Value) + )) + .ToArray() + ); + } + + public TSort? GetSort(ILeaseSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; +} diff --git a/src/EntityDb.Common/Queries/DeleteTagsQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs similarity index 61% rename from src/EntityDb.Common/Queries/DeleteTagsQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs index 36d80348..cf75f5b1 100644 --- a/src/EntityDb.Common/Queries/DeleteTagsQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs @@ -1,12 +1,13 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Common.Queries; +namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record DeleteTagsQuery(Id EntityId, IReadOnlyCollection Tags, object? Options = null) : ITagQuery +internal sealed record DeleteTagsQuery + (Id EntityId, IReadOnlyCollection Tags, object? Options = null) : ITagQuery { public TFilter GetFilter(ITagFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs new file mode 100644 index 00000000..f801d418 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs @@ -0,0 +1,38 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record GetDeltasQuery(Pointer EntityPointer, Version SnapshotVersion, + object? Options = null) : IMessageQuery +{ + public TFilter GetFilter(IMessageFilterBuilder builder) + { + var filters = new List + { + builder.EntityIdIn(EntityPointer.Id), builder.EntityVersionGte(EntityPointer.Version.Next()), + }; + + if (SnapshotVersion != Version.Zero) + { + filters.Add(builder.EntityVersionLte(SnapshotVersion)); + } + + return builder.And(filters.ToArray()); + } + + public TSort GetSort(IMessageSortBuilder builder) + { + return builder.Combine + ( + builder.EntityVersion(true) + ); + } + + public int? Skip => null; + + public int? Take => null; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs new file mode 100644 index 00000000..fb4a82f3 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs @@ -0,0 +1,23 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record GetLastEntityVersionQuery(Id EntityId, object? Options = null) : IMessageQuery +{ + public TFilter GetFilter(IMessageFilterBuilder builder) + { + return builder.EntityIdIn(EntityId); + } + + public TSort GetSort(IMessageSortBuilder builder) + { + return builder.EntityVersion(false); + } + + public int? Skip => null; + + public int? Take => 1; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs new file mode 100644 index 00000000..afc6baa4 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs @@ -0,0 +1,35 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record GetSourceQuery(Id SourceId) : IMessageGroupQuery, IMessageQuery +{ + public TFilter GetFilter(IMessageGroupFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + public TSort? GetSort(IMessageGroupSortBuilder builder) + { + return default; + } + + public int? Skip => default; + + public int? Take => default; + + public object? Options => default; + + public TFilter GetFilter(IMessageFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + public TSort? GetSort(IMessageSortBuilder builder) + { + return default; + } +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs new file mode 100644 index 00000000..06559bef --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs @@ -0,0 +1,76 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks.Dataflow; + +namespace EntityDb.Common.Sources.ReprocessorQueues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal sealed class BufferBlockSourceReprocessorQueue : BackgroundService, ISourceReprocessorQueue +{ + private readonly BufferBlock _bufferBlock = new(); + private readonly ILogger _logger; + private readonly ISourceProcessorQueue _sourceProcessorQueue; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + + public BufferBlockSourceReprocessorQueue(ILogger logger, + ISourceRepositoryFactory sourceRepositoryFactory, ISourceProcessorQueue sourceProcessorQueue) + { + _logger = logger; + _sourceRepositoryFactory = sourceRepositoryFactory; + _sourceProcessorQueue = sourceProcessorQueue; + } + + public void Enqueue(ISourceReprocessorQueueItem reprocessSourcesRequest) + { + _bufferBlock.Post(reprocessSourcesRequest); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (await _bufferBlock.OutputAvailableAsync(stoppingToken)) + { + var sourceReprocessorQueueItem = await _bufferBlock.ReceiveAsync(stoppingToken); + + await Process(sourceReprocessorQueueItem, stoppingToken); + } + } + + private async Task Process(ISourceReprocessorQueueItem item, CancellationToken cancellationToken) + { + try + { + _logger.LogDebug("Started reprocessing sources"); + + await using var sourceRepository = + await _sourceRepositoryFactory.CreateRepository(item.SourceSessionOptionsName, + cancellationToken); + + var sourceIds = await sourceRepository + .EnumerateSourceIds(item.Query, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var sourceId in sourceIds) + { + await Task.Delay(item.EnqueueDelay, cancellationToken); + + var source = await sourceRepository + .GetSource(sourceId, cancellationToken); + + _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem + { + SourceProcessorType = item.SourceProcessorType, Source = source, + }); + } + + _logger.LogDebug("Finished reprocessing sources"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess sources"); + } + } +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs deleted file mode 100644 index 7df87047..00000000 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockTransactionReprocessorQueue.cs +++ /dev/null @@ -1,74 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Extensions; -using EntityDb.Common.Sources.Processors.Queues; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks.Dataflow; - -namespace EntityDb.Common.Sources.ReprocessorQueues; - -[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] -internal class BufferBlockTransactionReprocessorQueue : BackgroundService, ITransactionReprocessorQueue -{ - private readonly BufferBlock _bufferBlock = new(); - private readonly ILogger _logger; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; - private readonly ISourceProcessorQueue _sourceProcessorQueue; - - public BufferBlockTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue sourceProcessorQueue) - { - _logger = logger; - _transactionRepositoryFactory = transactionRepositoryFactory; - _sourceProcessorQueue = sourceProcessorQueue; - } - - public void Enqueue(ITransactionReprocessorQueueItem reprocessTransactionsRequest) - { - _bufferBlock.Post(reprocessTransactionsRequest); - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (await _bufferBlock.OutputAvailableAsync(stoppingToken)) - { - var reprocessTransactionsRequest = await _bufferBlock.ReceiveAsync(stoppingToken); - - await Process(reprocessTransactionsRequest, stoppingToken); - } - } - - protected async Task Process(ITransactionReprocessorQueueItem item, CancellationToken cancellationToken) - { - try - { - _logger.LogDebug("Started reprocessing sources"); - - await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(item.TransactionSessionOptionsName, cancellationToken); - - var transactionIds = await transactionRepository - .EnumerateTransactionIds(item.Query, cancellationToken) - .ToArrayAsync(cancellationToken); - - foreach (var transactionId in transactionIds) - { - await Task.Delay(item.EnqueueDelay, cancellationToken); - - var transaction = await transactionRepository - .GetTransaction(transactionId, cancellationToken); - - _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem - { - SourceProcessorType = item.SourceProcessorType, - Source = transaction, - }); - } - - _logger.LogDebug("Finished reprocessing sources"); - } - catch (Exception exception) - { - _logger.LogError(exception, "Failed to reprocess sources"); - } - } -} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueue.cs new file mode 100644 index 00000000..7a0ec507 --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueue.cs @@ -0,0 +1,13 @@ +namespace EntityDb.Common.Sources.ReprocessorQueues; + +/// +/// A queue that reprocesses sources +/// +public interface ISourceReprocessorQueue +{ + /// + /// Enqueues the request to reprocess sources + /// + /// + void Enqueue(ISourceReprocessorQueueItem item); +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs new file mode 100644 index 00000000..d2394889 --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs @@ -0,0 +1,33 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Common.Sources.Processors; + +namespace EntityDb.Common.Sources.ReprocessorQueues; + +/// +/// Represents a request for a to reprocess sources. +/// +public interface ISourceReprocessorQueueItem +{ + /// + /// The name of the source session options passed to + /// + /// + string SourceSessionOptionsName { get; } + + /// + /// The type of the source processor, which *must* + /// implement . + /// + Type SourceProcessorType { get; } + + /// + /// Determines which sources need to be reprocessed. + /// + IQuery Query { get; } + + /// + /// Determines how long to wait between each call to enqueue. + /// + TimeSpan EnqueueDelay { get; } +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs deleted file mode 100644 index 198719db..00000000 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueue.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace EntityDb.Common.Sources.ReprocessorQueues; - -/// -/// A queue that reprocesses transactions -/// -public interface ITransactionReprocessorQueue -{ - /// - /// Enqueues the request to reprocess transactions - /// - /// - void Enqueue(ITransactionReprocessorQueueItem item); -} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs deleted file mode 100644 index 70ec98c5..00000000 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/ITransactionReprocessorQueueItem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Sources.Processors; - -namespace EntityDb.Common.Sources.ReprocessorQueues; - -/// -/// Represents a request for a to reprocess transactions. -/// -public interface ITransactionReprocessorQueueItem -{ - /// - /// The name of the transaction session options passed to - /// - string TransactionSessionOptionsName { get; } - - /// - /// The type of the transaction process, which *must* - /// implement . - /// - Type SourceProcessorType { get; } - - /// - /// Determines which transactions need to be reprocessed. - /// - IQuery Query { get; } - - /// - /// Determines how long to wait between each call to enqueue. - /// - TimeSpan EnqueueDelay { get; } - - /// - /// If true, then stop executing this request when the transaction processor throws an exception. - /// Otherwise, execute this request for all matching transactions. - /// - bool BreakOnThrow { get; } -} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs new file mode 100644 index 00000000..6d89217c --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs @@ -0,0 +1,59 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.ReprocessorQueues; + +internal class TestModeSourceReprocessorQueue : ISourceReprocessorQueue +{ + private readonly ILogger _logger; + private readonly ISourceProcessorQueue _sourceProcessorQueue; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + + public TestModeSourceReprocessorQueue(ILogger logger, + ISourceRepositoryFactory sourceRepositoryFactory, ISourceProcessorQueue sourceProcessorQueue) + { + _logger = logger; + _sourceRepositoryFactory = sourceRepositoryFactory; + _sourceProcessorQueue = sourceProcessorQueue; + } + + public void Enqueue(ISourceReprocessorQueueItem reprocessSourcesRequest) + { + Task.Run(() => Process(reprocessSourcesRequest, default)); + } + + private async Task Process(ISourceReprocessorQueueItem item, CancellationToken cancellationToken) + { + try + { + _logger.LogDebug("Started reprocessing sources"); + + await using var sourceRepository = + await _sourceRepositoryFactory.CreateRepository(item.SourceSessionOptionsName, + cancellationToken); + + var sourceIds = await sourceRepository + .EnumerateSourceIds(item.Query, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var sourceId in sourceIds) + { + var source = await sourceRepository + .GetSource(sourceId, cancellationToken); + + _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem + { + SourceProcessorType = item.SourceProcessorType, Source = source, + }); + } + + _logger.LogDebug("Finished reprocessing sources"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to reprocess sources"); + } + } +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs deleted file mode 100644 index 0c212284..00000000 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeTransactionReprocessorQueue.cs +++ /dev/null @@ -1,57 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Extensions; -using EntityDb.Common.Sources.Processors.Queues; -using Microsoft.Extensions.Logging; - -namespace EntityDb.Common.Sources.ReprocessorQueues; - -internal class TestModeTransactionReprocessorQueue : ITransactionReprocessorQueue -{ - private readonly ILogger _logger; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; - private readonly ISourceProcessorQueue _sourceProcessorQueue; - - public TestModeTransactionReprocessorQueue(ILogger logger, ITransactionRepositoryFactory transactionRepositoryFactory, ISourceProcessorQueue sourceProcessorQueue) - { - _logger = logger; - _transactionRepositoryFactory = transactionRepositoryFactory; - _sourceProcessorQueue = sourceProcessorQueue; - } - - public void Enqueue(ITransactionReprocessorQueueItem reprocessTransactionsRequest) - { - Task.Run(() => Process(reprocessTransactionsRequest, default)); - } - - protected async Task Process(ITransactionReprocessorQueueItem item, CancellationToken cancellationToken) - { - try - { - _logger.LogDebug("Started reprocessing sources"); - - await using var transactionRepository = await _transactionRepositoryFactory.CreateRepository(item.TransactionSessionOptionsName, cancellationToken); - - var transactionIds = await transactionRepository - .EnumerateTransactionIds(item.Query, cancellationToken) - .ToArrayAsync(cancellationToken); - - foreach (var transactionId in transactionIds) - { - var transaction = await transactionRepository - .GetTransaction(transactionId, cancellationToken); - - _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem - { - SourceProcessorType = item.SourceProcessorType, - Source = transaction, - }); - } - - _logger.LogDebug("Finished reprocessing sources"); - } - catch (Exception exception) - { - _logger.LogError(exception, "Failed to reprocess sources"); - } - } -} diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs new file mode 100644 index 00000000..91759632 --- /dev/null +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -0,0 +1,119 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.Sources; + +internal abstract class SourceRepositoryWrapper : DisposableResourceBaseClass, ISourceRepository +{ + private readonly ISourceRepository _sourceRepository; + + protected SourceRepositoryWrapper(ISourceRepository sourceRepository) + { + _sourceRepository = sourceRepository; + } + + public IAsyncEnumerable EnumerateSourceIds(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageGroupQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateSourceIds(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(leaseQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateEntityPointers(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(messageGroupQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateEntityPointers(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(messageQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateEntityPointers(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(leaseQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateEntityPointers(ITagQuery tagQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(tagQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateAgentSignatures(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(messageGroupQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateDeltas(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateLeases(leaseQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateTags(tagQuery, cancellationToken)); + } + + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => + _sourceRepository.EnumerateAnnotatedAgentSignatures(messageGroupQuery, cancellationToken)); + } + + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageQuery, cancellationToken)); + } + + public Task Commit(Source source, + CancellationToken cancellationToken = default) + { + return WrapCommand(() => _sourceRepository.Commit(source, cancellationToken)); + } + + public override async ValueTask DisposeAsync() + { + await _sourceRepository.DisposeAsync(); + } + + protected abstract IAsyncEnumerable WrapQuery(Func> enumerable); + + protected abstract Task WrapCommand(Func> task); +} diff --git a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs index 13b03bd4..b612cd6e 100644 --- a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs @@ -1,6 +1,5 @@ -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Entities; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; using EntityDb.Common.Extensions; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; @@ -17,9 +16,9 @@ public EntitySnapshotSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue _sourceProcessorQueue = sourceProcessorQueue; } - public void Notify(ISource source) + public void Notify(Source source) { - if (source is not ITransaction transaction || !transaction.Commands.Any(command => TEntity.CanReduce(command.Data))) + if (!source.Messages.Any(message => TEntity.CanReduce(message.Delta))) { return; } diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs index caa7dd9b..63c72067 100644 --- a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Sources; using EntityDb.Common.Extensions; -using EntityDb.Common.Projections; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; @@ -16,9 +16,9 @@ public ProjectionSnapshotSourceSubscriber(ISourceProcessorQueue sourceProcessorQ _sourceProcessorQueue = sourceProcessorQueue; } - public void Notify(ISource source) + public void Notify(Source source) { - if (!TProjection.EnumerateProjectionIds(source).Any()) + if (!TProjection.EnumerateEntityIds(source).Any()) { return; } diff --git a/src/EntityDb.Common/Transactions/TryCatchTransactionRepository.cs b/src/EntityDb.Common/Sources/TryCatchSourceRepository.cs similarity index 68% rename from src/EntityDb.Common/Transactions/TryCatchTransactionRepository.cs rename to src/EntityDb.Common/Sources/TryCatchSourceRepository.cs index 279bcd50..4292d5b3 100644 --- a/src/EntityDb.Common/Transactions/TryCatchTransactionRepository.cs +++ b/src/EntityDb.Common/Sources/TryCatchSourceRepository.cs @@ -1,18 +1,18 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace EntityDb.Common.Transactions; +namespace EntityDb.Common.Sources; -internal sealed class TryCatchTransactionRepository : TransactionRepositoryWrapper +internal sealed class TryCatchSourceRepository : SourceRepositoryWrapper { - private readonly ILogger _logger; + private readonly ILogger _logger; - public TryCatchTransactionRepository + public TryCatchSourceRepository ( - ITransactionRepository transactionRepository, - ILogger logger - ) : base(transactionRepository) + ISourceRepository sourceRepository, + ILogger logger + ) : base(sourceRepository) { _logger = logger; } @@ -72,10 +72,10 @@ protected override async Task WrapCommand(Func> task) } } - public static ITransactionRepository Create(IServiceProvider serviceProvider, - ITransactionRepository transactionRepository) + public static ISourceRepository Create(IServiceProvider serviceProvider, + ISourceRepository sourceRepository) { - return ActivatorUtilities.CreateInstance(serviceProvider, - transactionRepository); + return ActivatorUtilities.CreateInstance(serviceProvider, + sourceRepository); } } diff --git a/src/EntityDb.Common/Tags/Tag.cs b/src/EntityDb.Common/Tags/Tag.cs deleted file mode 100644 index 97fc6dbc..00000000 --- a/src/EntityDb.Common/Tags/Tag.cs +++ /dev/null @@ -1,6 +0,0 @@ -using EntityDb.Abstractions.Tags; - -namespace EntityDb.Common.Tags; - -/// -public sealed record Tag(string Label, string Value) : ITag; diff --git a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs deleted file mode 100644 index c42ba394..00000000 --- a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs +++ /dev/null @@ -1,49 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; - -namespace EntityDb.Common.Transactions.Builders; - -internal sealed class SingleEntityTransactionBuilder : ISingleEntityTransactionBuilder - where TEntity : IEntity -{ - private readonly ITransactionBuilder _transactionBuilder; - - internal SingleEntityTransactionBuilder(ITransactionBuilder transactionBuilder, Id entityId) - { - _transactionBuilder = transactionBuilder; - EntityId = entityId; - } - - public Id EntityId { get; } - - public TEntity GetEntity() - { - return _transactionBuilder.GetEntity(EntityId); - } - - public bool IsEntityKnown() - { - return _transactionBuilder.IsEntityKnown(EntityId); - } - - public ISingleEntityTransactionBuilder Load(TEntity entity) - { - _transactionBuilder.Load(EntityId, entity); - - return this; - } - - public ISingleEntityTransactionBuilder Append(object command) - { - _transactionBuilder.Append(EntityId, command); - - return this; - } - - public ITransaction Build(Id transactionId) - { - return _transactionBuilder.Build(transactionId); - } -} diff --git a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs b/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs deleted file mode 100644 index 77a4f691..00000000 --- a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs +++ /dev/null @@ -1,105 +0,0 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; -using EntityDb.Common.Exceptions; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions.Builders; - -internal sealed class TransactionBuilder : ITransactionBuilder - where TEntity : IEntity -{ - private readonly IAgent _agent; - private readonly Dictionary _knownEntities = new(); - private readonly List _transactionCommands = new(); - - public TransactionBuilder(IAgent agent) - { - _agent = agent; - } - - public TEntity GetEntity(Id entityId) - { - return _knownEntities[entityId]; - } - - public bool IsEntityKnown(Id entityId) - { - return _knownEntities.ContainsKey(entityId); - } - - public ITransactionBuilder Load(Id entityId, TEntity entity) - { - if (IsEntityKnown(entityId)) - { - throw new EntityAlreadyKnownException(); - } - - _knownEntities.Add(entityId, entity); - - return this; - } - - public ITransactionBuilder Append(Id entityId, object command) - { - ConstructIfNotKnown(entityId); - - var entity = _knownEntities[entityId].Reduce(command); - var entityVersionNumber = entity.GetVersionNumber(); - - _transactionCommands.Add(new TransactionCommand - { - EntityId = entityId, - EntityVersionNumber = entityVersionNumber, - Data = command, - AddLeases = command is IAddLeasesCommand addLeasesCommand - ? addLeasesCommand.GetLeases(entity).ToImmutableArray() - : ImmutableArray.Empty, - AddTags = command is IAddTagsCommand addTagsCommand - ? addTagsCommand.GetTags(entity).ToImmutableArray() - : ImmutableArray.Empty, - DeleteLeases = command is IDeleteLeasesCommand deleteLeasesCommand - ? deleteLeasesCommand.GetLeases(entity).ToImmutableArray() - : ImmutableArray.Empty, - DeleteTags = command is IDeleteTagsCommand deleteTagsCommand - ? deleteTagsCommand.GetTags(entity).ToImmutableArray() - : ImmutableArray.Empty, - }); - - _knownEntities[entityId] = entity; - - return this; - } - - public ITransaction Build(Id transactionId) - { - var transaction = new Transaction - { - Id = transactionId, - TimeStamp = _agent.TimeStamp, - AgentSignature = _agent.Signature, - Commands = _transactionCommands.ToImmutableArray() - }; - - _transactionCommands.Clear(); - - return transaction; - } - - private void ConstructIfNotKnown(Id entityId) - { - if (IsEntityKnown(entityId)) - { - return; - } - - var entity = TEntity.Construct(entityId); - - _knownEntities.Add(entityId, entity); - } -} diff --git a/src/EntityDb.Common/Transactions/Builders/TransactionBuilderFactory.cs b/src/EntityDb.Common/Transactions/Builders/TransactionBuilderFactory.cs deleted file mode 100644 index 8820f425..00000000 --- a/src/EntityDb.Common/Transactions/Builders/TransactionBuilderFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; - -namespace EntityDb.Common.Transactions.Builders; - -internal sealed class TransactionBuilderFactory : ITransactionBuilderFactory - where TEntity : IEntity -{ - private readonly IAgentAccessor _agentAccessor; - - public TransactionBuilderFactory(IAgentAccessor agentAccessor) - { - _agentAccessor = agentAccessor; - } - - public async Task> Create(string agentSignatureOptionsName, - CancellationToken cancellationToken) - { - var agent = await _agentAccessor.GetAgentAsync(agentSignatureOptionsName, cancellationToken); - - return new TransactionBuilder(agent); - } - - public async Task> CreateForSingleEntity(string agentSignatureOptionsName, - Id entityId, CancellationToken cancellationToken) - { - var transactionBuilder = await Create(agentSignatureOptionsName, cancellationToken); - - return new SingleEntityTransactionBuilder(transactionBuilder, entityId); - } -} diff --git a/src/EntityDb.Common/Transactions/Transaction.cs b/src/EntityDb.Common/Transactions/Transaction.cs deleted file mode 100644 index 1af6c8b8..00000000 --- a/src/EntityDb.Common/Transactions/Transaction.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions; - -internal sealed record Transaction : ITransaction -{ - public Id Id { get; init; } - public TimeStamp TimeStamp { get; init; } - public object AgentSignature { get; init; } = default!; - public ImmutableArray Commands { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/TransactionCommand.cs b/src/EntityDb.Common/Transactions/TransactionCommand.cs deleted file mode 100644 index b835d73b..00000000 --- a/src/EntityDb.Common/Transactions/TransactionCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using System.Collections.Immutable; - -namespace EntityDb.Common.Transactions; - -internal record TransactionCommand : ITransactionCommand -{ - public required Id EntityId { get; init; } - public required VersionNumber EntityVersionNumber { get; init; } - public required object Data { get; init; } = default!; - public required ImmutableArray AddLeases { get; init; } - public required ImmutableArray AddTags { get; init; } - public required ImmutableArray DeleteLeases { get; init; } - public required ImmutableArray DeleteTags { get; init; } -} diff --git a/src/EntityDb.Common/Transactions/TransactionRepositoryWrapper.cs b/src/EntityDb.Common/Transactions/TransactionRepositoryWrapper.cs deleted file mode 100644 index 39cf5811..00000000 --- a/src/EntityDb.Common/Transactions/TransactionRepositoryWrapper.cs +++ /dev/null @@ -1,117 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; - -namespace EntityDb.Common.Transactions; - -internal abstract class TransactionRepositoryWrapper : DisposableResourceBaseClass, ITransactionRepository -{ - private readonly ITransactionRepository _transactionRepository; - - protected TransactionRepositoryWrapper(ITransactionRepository transactionRepository) - { - _transactionRepository = transactionRepository; - } - - public IAsyncEnumerable EnumerateTransactionIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateTransactionIds(agentSignatureQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateTransactionIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateTransactionIds(commandQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateTransactionIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateTransactionIds(leaseQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateTransactionIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateTransactionIds(tagQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateEntityIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateEntityIds(agentSignatureQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateEntityIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateEntityIds(commandQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateEntityIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateEntityIds(leaseQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateEntityIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateEntityIds(tagQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateAgentSignatures(agentSignatureQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateCommands(commandQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateLeases(leaseQuery, cancellationToken)); - } - - public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateTags(tagQuery, cancellationToken)); - } - - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateAnnotatedAgentSignatures(agentSignatureQuery, cancellationToken)); - } - - public IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _transactionRepository.EnumerateAnnotatedCommands(commandQuery, cancellationToken)); - } - - public Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default) - { - return WrapCommand(() => _transactionRepository.PutTransaction(transaction, cancellationToken)); - } - - public override async ValueTask DisposeAsync() - { - await _transactionRepository.DisposeAsync(); - } - - protected abstract IAsyncEnumerable WrapQuery(Func> enumerable); - - protected abstract Task WrapCommand(Func> task); -} diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs index 34cf10d8..e2edfac6 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs @@ -13,17 +13,7 @@ public DefaultPartialTypeResolver(IOptions op { _options = options; } - - private Assembly AssemblyResolver(AssemblyName assemblyName) - { - if (_options.Value.IgnoreVersion) - { - assemblyName.Version = null; - } - - return Assembly.Load(assemblyName); - } - + public bool TryResolveType(EnvelopeHeaders envelopeHeaders, [NotNullWhen(true)] out Type? resolvedType) { if (EnvelopeHelper.NotThisPlatform(envelopeHeaders) || @@ -45,4 +35,14 @@ public bool TryResolveType(EnvelopeHeaders envelopeHeaders, [NotNullWhen(true)] return true; } + + private Assembly AssemblyResolver(AssemblyName assemblyName) + { + if (_options.Value.IgnoreVersion) + { + assemblyName.Version = null; + } + + return Assembly.Load(assemblyName); + } } diff --git a/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs b/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs index 9c3c151e..7d7132e1 100644 --- a/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs +++ b/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs @@ -14,10 +14,10 @@ public InMemorySnapshotRepository(IInMemorySession inMemorySession) _inMemorySession = inMemorySession; } - public Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, + public Task PutSnapshot(Pointer subjectPointer, TSnapshot snapshot, CancellationToken cancellationToken = default) { - return _inMemorySession.Insert(snapshotPointer, snapshot).WaitAsync(cancellationToken); + return _inMemorySession.Insert(subjectPointer, snapshot).WaitAsync(cancellationToken); } public Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default) diff --git a/src/EntityDb.Json/Converters/VersionNumberConverter.cs b/src/EntityDb.Json/Converters/VersionConverter.cs similarity index 67% rename from src/EntityDb.Json/Converters/VersionNumberConverter.cs rename to src/EntityDb.Json/Converters/VersionConverter.cs index c4d097f9..201bddea 100644 --- a/src/EntityDb.Json/Converters/VersionNumberConverter.cs +++ b/src/EntityDb.Json/Converters/VersionConverter.cs @@ -1,12 +1,12 @@ -using EntityDb.Abstractions.ValueObjects; using System.Text.Json; using System.Text.Json.Serialization; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Json.Converters; -internal class VersionNumberConverter : JsonConverter +internal class VersionConverter : JsonConverter { - public override VersionNumber Read + public override Version Read ( ref Utf8JsonReader reader, Type typeToConvert, @@ -20,17 +20,17 @@ JsonSerializerOptions options var ulongValue = reader.GetUInt64(); - return new VersionNumber(ulongValue); + return new Version(ulongValue); } public override void Write ( Utf8JsonWriter writer, - VersionNumber versionNumber, + Version version, JsonSerializerOptions options ) { - var ulongValue = versionNumber.Value; + var ulongValue = version.Value; writer.WriteNumberValue(ulongValue); } diff --git a/src/EntityDb.Json/EntityDb.Json.csproj b/src/EntityDb.Json/EntityDb.Json.csproj index 1b457854..5370bc02 100644 --- a/src/EntityDb.Json/EntityDb.Json.csproj +++ b/src/EntityDb.Json/EntityDb.Json.csproj @@ -1,7 +1,7 @@  - - - + + + diff --git a/src/EntityDb.Json/Envelopes/JsonBytesEnvelopeService.cs b/src/EntityDb.Json/Envelopes/JsonBytesEnvelopeService.cs index e0a6dcc5..fad35060 100644 --- a/src/EntityDb.Json/Envelopes/JsonBytesEnvelopeService.cs +++ b/src/EntityDb.Json/Envelopes/JsonBytesEnvelopeService.cs @@ -7,14 +7,15 @@ namespace EntityDb.Json.Envelopes; internal sealed class JsonBytesEnvelopeService : JsonEnvelopeService { - public JsonBytesEnvelopeService(ILogger logger, ITypeResolver typeResolver) : base(logger, typeResolver) + public JsonBytesEnvelopeService(ILogger logger, ITypeResolver typeResolver) : base(logger, + typeResolver) { } protected override Envelope DeserializeEnvelope(byte[] serializedData) { return (Envelope)JsonSerializer.Deserialize(serializedData, typeof(Envelope), - JsonSerializerOptions)!; + JsonSerializerOptions)!; } protected override byte[] SerializeEnvelope(Envelope envelope) diff --git a/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs b/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs index f9a4a9e6..d66075b9 100644 --- a/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs +++ b/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs @@ -8,21 +8,24 @@ namespace EntityDb.Json.Envelopes; -internal abstract class JsonEnvelopeService : IEnvelopeService +internal abstract class JsonEnvelopeService { - private readonly ILogger> _logger; - private readonly ITypeResolver _typeResolver; - protected static readonly JsonSerializerOptions JsonSerializerOptions = new() { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; +} + +internal abstract class JsonEnvelopeService : JsonEnvelopeService, IEnvelopeService +{ + private readonly ILogger> _logger; + private readonly ITypeResolver _typeResolver; static JsonEnvelopeService() { JsonSerializerOptions.Converters.Add(new EnvelopeHeadersConverter()); JsonSerializerOptions.Converters.Add(new IdConverter()); - JsonSerializerOptions.Converters.Add(new VersionNumberConverter()); + JsonSerializerOptions.Converters.Add(new VersionConverter()); } protected JsonEnvelopeService @@ -35,10 +38,6 @@ ITypeResolver typeResolver _typeResolver = typeResolver; } - protected abstract TSerializedData SerializeEnvelope(Envelope envelope); - - protected abstract Envelope DeserializeEnvelope(TSerializedData serializedData); - public TSerializedData Serialize(TData data) { try @@ -79,4 +78,8 @@ public TData Deserialize(TSerializedData serializedData) throw new DeserializeException(); } } + + protected abstract TSerializedData SerializeEnvelope(Envelope envelope); + + protected abstract Envelope DeserializeEnvelope(TSerializedData serializedData); } diff --git a/src/EntityDb.Json/Envelopes/JsonStringEnvelopeService.cs b/src/EntityDb.Json/Envelopes/JsonStringEnvelopeService.cs index 55a37b5a..7cc16291 100644 --- a/src/EntityDb.Json/Envelopes/JsonStringEnvelopeService.cs +++ b/src/EntityDb.Json/Envelopes/JsonStringEnvelopeService.cs @@ -7,14 +7,15 @@ namespace EntityDb.Json.Envelopes; internal sealed class JsonStringEnvelopeService : JsonEnvelopeService { - public JsonStringEnvelopeService(ILogger logger, ITypeResolver typeResolver) : base(logger, typeResolver) + public JsonStringEnvelopeService(ILogger logger, ITypeResolver typeResolver) : base( + logger, typeResolver) { } protected override Envelope DeserializeEnvelope(string serializedData) { return (Envelope)JsonSerializer.Deserialize(serializedData, typeof(Envelope), - JsonSerializerOptions)!; + JsonSerializerOptions)!; } protected override string SerializeEnvelope(Envelope envelope) diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 21afc843..3cb0c106 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -1,42 +1,46 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Common.Envelopes; -using EntityDb.MongoDb.Commands; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Queries.FilterBuilders; -using EntityDb.MongoDb.Queries.SortBuilders; +using EntityDb.MongoDb.Documents.Commands; +using EntityDb.MongoDb.Documents.Queries; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Queries.SortBuilders; using MongoDB.Bson; namespace EntityDb.MongoDb.Documents; -internal sealed record AgentSignatureDocument : DocumentBase, IEntitiesDocument +internal sealed record AgentSignatureDocument : MessageGroupDocumentBase { public const string CollectionName = "AgentSignatures"; - private static readonly AgentSignatureFilterBuilder FilterBuilder = new(); - - private static readonly AgentSignatureSortBuilder SortBuilder = new(); - - public Id[] EntityIds { get; init; } = default!; + private static readonly MessageGroupSortBuilder SortBuilder = new(); public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, - ITransaction transaction + Source source ) { + var entityPointers = source.Messages + .Select(message => message.EntityPointer) + .Distinct() + .ToArray(); + + var entityIds = entityPointers + .Select(entityPointer => entityPointer.Id) + .ToArray(); + var documents = new[] { new AgentSignatureDocument { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityIds = transaction.Commands.Select(transactionCommand => transactionCommand.EntityId).Distinct() - .ToArray(), - DataType = transaction.AgentSignature.GetType().Name, - Data = envelopeService.Serialize(transaction.AgentSignature) - } + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + EntityIds = entityIds, + EntityPointers = entityPointers, + DataType = source.AgentSignature.GetType().Name, + Data = envelopeService.Serialize(source.AgentSignature), + }, }; return new InsertDocumentsCommand @@ -48,17 +52,17 @@ ITransaction transaction public static DocumentQuery GetQuery ( - IAgentSignatureQuery agentSignatureQuery + IMessageGroupQuery messageGroupQuery ) { return new DocumentQuery ( CollectionName, - agentSignatureQuery.GetFilter(FilterBuilder), - agentSignatureQuery.GetSort(SortBuilder), - agentSignatureQuery.Skip, - agentSignatureQuery.Take, - agentSignatureQuery.Options as MongoDbQueryOptions + messageGroupQuery.GetFilter(FilterBuilder), + messageGroupQuery.GetSort(SortBuilder), + messageGroupQuery.Skip, + messageGroupQuery.Take, + messageGroupQuery.Options as MongoDbQueryOptions ); } } diff --git a/src/EntityDb.MongoDb/Documents/CommandDocument.cs b/src/EntityDb.MongoDb/Documents/CommandDocument.cs deleted file mode 100644 index cf0980ff..00000000 --- a/src/EntityDb.MongoDb/Documents/CommandDocument.cs +++ /dev/null @@ -1,87 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Queries; -using EntityDb.MongoDb.Commands; -using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Queries.FilterBuilders; -using EntityDb.MongoDb.Queries.SortBuilders; -using EntityDb.MongoDb.Transactions.Sessions; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Documents; - -internal sealed record CommandDocument : DocumentBase, IEntityDocument -{ - public const string CollectionName = "Commands"; - - private static readonly CommandFilterBuilder FilterBuilder = new(); - - private static readonly CommandSortBuilder SortBuilder = new(); - - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } - - public static InsertDocumentsCommand GetInsertCommand - ( - IEnvelopeService envelopeService, - ITransaction transaction, - ITransactionCommand transactionCommand - ) - { - var documents = new[] - { - new CommandDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, - DataType = transactionCommand.Data.GetType().Name, - Data = envelopeService.Serialize(transactionCommand.Data) - } - }; - - return new InsertDocumentsCommand - ( - CollectionName, - documents - ); - } - - public static DocumentQuery GetQuery - ( - ICommandQuery commandQuery - ) - { - return new DocumentQuery - ( - CollectionName, - commandQuery.GetFilter(FilterBuilder), - commandQuery.GetSort(SortBuilder), - commandQuery.Skip, - commandQuery.Take, - commandQuery.Options as MongoDbQueryOptions - ); - } - - public static async Task GetLastEntityVersionNumber - ( - IMongoSession mongoSession, - Id entityId, - CancellationToken cancellationToken - ) - { - var commandQuery = new GetLastEntityCommandQuery(entityId); - - var documentQuery = GetQuery(commandQuery); - - var document = await documentQuery - .Execute(mongoSession, DocumentQueryExtensions.EntityVersionNumberProjection, cancellationToken) - .SingleOrDefaultAsync(cancellationToken); - - return document?.EntityVersionNumber ?? default; - } -} diff --git a/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs similarity index 77% rename from src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs rename to src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs index a3e873e2..33ccf8a1 100644 --- a/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs @@ -1,8 +1,8 @@ -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Sessions; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Commands; +namespace EntityDb.MongoDb.Documents.Commands; internal record DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs similarity index 74% rename from src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs rename to src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs index e8c305dd..4a3b26c0 100644 --- a/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs @@ -1,6 +1,6 @@ -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Sessions; -namespace EntityDb.MongoDb.Commands; +namespace EntityDb.MongoDb.Documents.Commands; internal record InsertDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Documents/DeltaDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDocument.cs new file mode 100644 index 00000000..f422b22b --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/DeltaDocument.cs @@ -0,0 +1,92 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.MongoDb.Documents.Commands; +using EntityDb.MongoDb.Documents.Queries; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Queries.FilterBuilders; +using EntityDb.MongoDb.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Sources.Sessions; +using MongoDB.Bson; +using MongoDB.Driver; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.MongoDb.Documents; + +internal sealed record DeltaDocument : MessageDocumentBase +{ + public const string CollectionName = "Deltas"; + + private static readonly ProjectionDefinition EntityVersionProjection = + ProjectionBuilder.Combine + ( + ProjectionBuilder.Exclude(nameof(_id)), + ProjectionBuilder.Include(nameof(EntityVersion)) + ); + + private static readonly MessageFilterBuilder FilterBuilder = new(); + + private static readonly MessageSortBuilder SortBuilder = new(); + + public static InsertDocumentsCommand GetInsertCommand + ( + IEnvelopeService envelopeService, + Source source, + Message message + ) + { + var documents = new[] + { + new DeltaDocument + { + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + EntityId = message.EntityPointer.Id, + EntityVersion = message.EntityPointer.Version, + EntityPointer = message.EntityPointer, + DataType = message.Delta.GetType().Name, + Data = envelopeService.Serialize(message.Delta), + }, + }; + + return new InsertDocumentsCommand + ( + CollectionName, + documents + ); + } + + public static DocumentQuery GetQuery + ( + IMessageQuery messageQuery + ) + { + return new DocumentQuery + ( + CollectionName, + messageQuery.GetFilter(FilterBuilder), + messageQuery.GetSort(SortBuilder), + messageQuery.Skip, + messageQuery.Take, + messageQuery.Options as MongoDbQueryOptions + ); + } + + public static async Task GetLastEntityVersion + ( + IMongoSession mongoSession, + Id entityId, + CancellationToken cancellationToken + ) + { + var lastEntityVersionQuery = new GetLastEntityVersionQuery(entityId); + + var document = await GetQuery(lastEntityVersionQuery) + .Execute(mongoSession, EntityVersionProjection, cancellationToken) + .SingleOrDefaultAsync(cancellationToken); + + return document?.EntityVersion ?? default; + } +} diff --git a/src/EntityDb.MongoDb/Documents/DocumentBase.cs b/src/EntityDb.MongoDb/Documents/DocumentBase.cs index 506bdae3..ceb7c91d 100644 --- a/src/EntityDb.MongoDb/Documents/DocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/DocumentBase.cs @@ -1,15 +1,33 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Documents; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; namespace EntityDb.MongoDb.Documents; -internal abstract record DocumentBase +internal abstract record DocumentBase : IDocument { - [BsonIgnoreIfNull] public ObjectId? _id { get; init; } + protected static readonly ProjectionDefinitionBuilder ProjectionBuilder = + Builders.Projection; + + public static ProjectionDefinition NoIdProjection { get; } = + ProjectionBuilder.Exclude(nameof(_id)); + + public static ProjectionDefinition SourceIdProjection { get; } = + ProjectionBuilder.Include(nameof(SourceId)); - public TimeStamp TransactionTimeStamp { get; init; } - public Id TransactionId { get; init; } - public string DataType { get; init; } = default!; - public BsonDocument Data { get; init; } = default!; + public static ProjectionDefinition DataProjection { get; } = + ProjectionBuilder.Combine + ( + ProjectionBuilder.Exclude(nameof(_id)), + ProjectionBuilder.Include(nameof(Data)) + ); + + // ReSharper disable once InconsistentNaming + [BsonIgnoreIfNull] public ObjectId? _id { get; init; } + public required string DataType { get; init; } + public required Id SourceId { get; init; } + public required TimeStamp SourceTimeStamp { get; init; } + public required BsonDocument Data { get; init; } } diff --git a/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs similarity index 84% rename from src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs rename to src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs index ebab2978..89cdfc8d 100644 --- a/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs +++ b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs @@ -1,14 +1,15 @@ using EntityDb.Common.Envelopes; using EntityDb.Common.Exceptions; using EntityDb.Common.TypeResolvers; -using EntityDb.MongoDb.Serializers; +using EntityDb.MongoDb.Documents.Serializers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; +using VersionSerializer = EntityDb.MongoDb.Documents.Serializers.VersionSerializer; -namespace EntityDb.MongoDb.Envelopes; +namespace EntityDb.MongoDb.Documents.Envelopes; internal class MongoDbEnvelopeService : IEnvelopeService { @@ -24,7 +25,7 @@ static MongoDbEnvelopeService() BsonSerializer.RegisterSerializer(new EnvelopeSerializer()); BsonSerializer.RegisterSerializer(new IdSerializer()); BsonSerializer.RegisterSerializer(new TimeStampSerializer()); - BsonSerializer.RegisterSerializer(new VersionNumberSerializer()); + BsonSerializer.RegisterSerializer(new VersionSerializer()); } public MongoDbEnvelopeService @@ -68,7 +69,8 @@ public TData Deserialize(BsonDocument serializedData) { try { - var envelope = (Envelope)BsonSerializer.Deserialize(serializedData, typeof(Envelope)); + var envelope = + (Envelope)BsonSerializer.Deserialize(serializedData, typeof(Envelope)); return (TData)BsonSerializer.Deserialize(envelope.Value, _typeResolver.ResolveType(envelope.Headers)); } diff --git a/src/EntityDb.MongoDb/Documents/IEntitiesDocument.cs b/src/EntityDb.MongoDb/Documents/IEntitiesDocument.cs deleted file mode 100644 index eac8e58f..00000000 --- a/src/EntityDb.MongoDb/Documents/IEntitiesDocument.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EntityDb.Common.Documents; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Documents; - -internal interface IEntitiesDocument : IEntitiesDocument, ITransactionDocument -{ -} diff --git a/src/EntityDb.MongoDb/Documents/IEntityDocument.cs b/src/EntityDb.MongoDb/Documents/IEntityDocument.cs deleted file mode 100644 index bb15331e..00000000 --- a/src/EntityDb.MongoDb/Documents/IEntityDocument.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EntityDb.Common.Documents; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Documents; - -internal interface IEntityDocument : IEntityDocument, ITransactionDocument -{ -} diff --git a/src/EntityDb.MongoDb/Documents/ITransactionDocument.cs b/src/EntityDb.MongoDb/Documents/ITransactionDocument.cs deleted file mode 100644 index cdc6d3b0..00000000 --- a/src/EntityDb.MongoDb/Documents/ITransactionDocument.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EntityDb.Common.Documents; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Documents; - -internal interface ITransactionDocument : ITransactionDocument -{ -#pragma warning disable IDE1006 // Naming Styles - // ReSharper disable once InconsistentNaming - ObjectId? _id { get; } -#pragma warning restore IDE1006 // Naming Styles -} diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs index b7aee044..2f08927a 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs @@ -1,18 +1,17 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Common.Envelopes; -using EntityDb.Common.Queries; -using EntityDb.MongoDb.Commands; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Queries.FilterBuilders; -using EntityDb.MongoDb.Queries.SortBuilders; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.MongoDb.Documents.Commands; +using EntityDb.MongoDb.Documents.Queries; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Queries.FilterBuilders; +using EntityDb.MongoDb.Sources.Queries.SortBuilders; using MongoDB.Bson; namespace EntityDb.MongoDb.Documents; -internal sealed record LeaseDocument : DocumentBase, IEntityDocument +internal sealed record LeaseDocument : MessageDocumentBase { public const string CollectionName = "Leases"; @@ -20,31 +19,30 @@ internal sealed record LeaseDocument : DocumentBase, IEntityDocument private static readonly LeaseSortBuilder SortBuilder = new(); - public string Scope { get; init; } = default!; - public string Label { get; init; } = default!; - public string Value { get; init; } = default!; - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } + public required string Scope { get; init; } + public required string Label { get; init; } + public required string Value { get; init; } public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, - ITransaction transaction, - ITransactionCommand transactionCommand + Source source, + Message message ) { - var leaseDocuments = transactionCommand.AddLeases + var leaseDocuments = message.AddLeases .Select(lease => new LeaseDocument { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + EntityId = message.EntityPointer.Id, + EntityVersion = message.EntityPointer.Version, + EntityPointer = message.EntityPointer, DataType = lease.GetType().Name, Data = envelopeService.Serialize(lease), Scope = lease.Scope, Label = lease.Label, - Value = lease.Value + Value = lease.Value, }) .ToArray(); @@ -73,11 +71,10 @@ leaseQuery.Options as MongoDbQueryOptions public static DeleteDocumentsCommand GetDeleteCommand ( - ITransactionCommand transactionCommand + Message message ) { - var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.EntityId, transactionCommand.DeleteLeases); + var deleteLeasesQuery = new DeleteLeasesQuery(message.DeleteLeases); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs new file mode 100644 index 00000000..92c91dae --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Documents; +using MongoDB.Bson; +using MongoDB.Driver; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.MongoDb.Documents; + +internal abstract record MessageDocumentBase : DocumentBase, IMessageDocument +{ + public static ProjectionDefinition EntityPointerProjection { get; } = + ProjectionBuilder.Include(nameof(EntityPointer)); + + public required Id EntityId { get; init; } + public required Version EntityVersion { get; init; } + public required Pointer EntityPointer { get; init; } +} diff --git a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs new file mode 100644 index 00000000..2dcf4693 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Documents; +using EntityDb.MongoDb.Sources.Queries.FilterBuilders; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Documents; + +internal abstract record MessageGroupDocumentBase : DocumentBase, IMessageGroupDocument +{ + protected static readonly MessageGroupFilterBuilder FilterBuilder = new(); + + public static ProjectionDefinition EntityPointersProjection { get; } = + ProjectionBuilder.Include(nameof(EntityPointers)); + + public required Id[] EntityIds { get; init; } + public required Pointer[] EntityPointers { get; init; } +} diff --git a/src/EntityDb.MongoDb/Queries/DocumentQuery.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs similarity index 63% rename from src/EntityDb.MongoDb/Queries/DocumentQuery.cs rename to src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs index f8e4cdec..335efdbe 100644 --- a/src/EntityDb.MongoDb/Queries/DocumentQuery.cs +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs @@ -1,8 +1,9 @@ -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Sessions; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Queries; +namespace EntityDb.MongoDb.Documents.Queries; internal record DocumentQuery ( @@ -14,7 +15,8 @@ internal record DocumentQuery MongoDbQueryOptions? Options ) { - public IAsyncEnumerable Execute(IMongoSession mongoSession, ProjectionDefinition projection, CancellationToken cancellationToken) + public IAsyncEnumerable Execute(IMongoSession mongoSession, + ProjectionDefinition projection, CancellationToken cancellationToken) { return mongoSession.Find(CollectionName, Filter, projection, Sort, Skip, Limit, Options, cancellationToken); } diff --git a/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs new file mode 100644 index 00000000..d7b1d362 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs @@ -0,0 +1,151 @@ +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Polyfills; +using EntityDb.Common.Sources.Documents; +using EntityDb.MongoDb.Sources.Sessions; +using MongoDB.Bson; +using MongoDB.Driver; +using System.Runtime.CompilerServices; + +namespace EntityDb.MongoDb.Documents.Queries; + +internal static class DocumentQueryExtensions +{ + private static IAsyncEnumerable EnumerateIds + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + ProjectionDefinition projection, + Func, IAsyncEnumerable> mapToIds, + CancellationToken cancellationToken + ) + { + var skip = documentQuery.Skip; + var limit = documentQuery.Limit; + + documentQuery = documentQuery with { Skip = null, Limit = null }; + + var documents = documentQuery.Execute(mongoSession, projection, cancellationToken); + + return documents.EnumerateIds(skip, limit, mapToIds); + } + + private static IAsyncEnumerable EnumeratePointers + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + ProjectionDefinition projection, + Func, IAsyncEnumerable> mapToPointers, + CancellationToken cancellationToken + ) + { + var skip = documentQuery.Skip; + var limit = documentQuery.Limit; + + documentQuery = documentQuery with { Skip = null, Limit = null }; + + var documents = documentQuery.Execute(mongoSession, projection, cancellationToken); + + return documents.EnumeratePointers(skip, limit, mapToPointers); + } + + public static IAsyncEnumerable EnumerateSourceIds + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + CancellationToken cancellationToken + ) + where TDocument : DocumentBase + { + return documentQuery.EnumerateIds + ( + mongoSession, + DocumentBase.SourceIdProjection, + documents => documents.Select(document => document.SourceId), + cancellationToken + ); + } + + public static IAsyncEnumerable EnumerateMessageGroupEntityPointers + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + CancellationToken cancellationToken + ) + where TDocument : MessageGroupDocumentBase + { + return documentQuery.EnumeratePointers + ( + mongoSession, + MessageGroupDocumentBase.EntityPointersProjection, + documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.EntityPointers)), + cancellationToken + ); + } + + public static IAsyncEnumerable EnumerateMessageEntityPointers + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + CancellationToken cancellationToken + ) + where TDocument : MessageDocumentBase + { + return documentQuery.EnumeratePointers + ( + mongoSession, + MessageDocumentBase.EntityPointerProjection, + documents => documents.Select(document => document.EntityPointer), + cancellationToken + ); + } + + public static async IAsyncEnumerable EnumerateData + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + IEnvelopeService envelopeService, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + where TDocument : DocumentBase + { + var documents = documentQuery.Execute(mongoSession, DocumentBase.DataProjection, cancellationToken); + + await foreach (var document in documents) + { + yield return envelopeService.Deserialize(document.Data); + } + } + + public static IAsyncEnumerable> EnumerateEntityAnnotation + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + IEnvelopeService envelopeService, + CancellationToken cancellationToken + ) + where TDocument : MessageDocumentBase + where TData : notnull + { + var documents = documentQuery.Execute(mongoSession, DocumentBase.NoIdProjection, cancellationToken); + + return documents.EnumerateEntityAnnotation(envelopeService, cancellationToken); + } + + public static IAsyncEnumerable> EnumerateEntitiesAnnotation + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + IEnvelopeService envelopeService, + CancellationToken cancellationToken + ) + where TDocument : MessageGroupDocumentBase + where TData : notnull + { + var documents = documentQuery.Execute(mongoSession, DocumentBase.NoIdProjection, cancellationToken); + + return documents.EnumerateEntitiesAnnotation(envelopeService, + cancellationToken); + } +} diff --git a/src/EntityDb.MongoDb/Serializers/EnvelopeSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs similarity index 95% rename from src/EntityDb.MongoDb/Serializers/EnvelopeSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs index 95a9104a..b6890820 100644 --- a/src/EntityDb.MongoDb/Serializers/EnvelopeSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs @@ -4,7 +4,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -namespace EntityDb.MongoDb.Serializers; +namespace EntityDb.MongoDb.Documents.Serializers; internal class EnvelopeSerializer : IBsonSerializer> { diff --git a/src/EntityDb.MongoDb/Serializers/IdSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs similarity index 91% rename from src/EntityDb.MongoDb/Serializers/IdSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs index fb029335..92e5b27d 100644 --- a/src/EntityDb.MongoDb/Serializers/IdSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.ValueObjects; using MongoDB.Bson.Serialization; -namespace EntityDb.MongoDb.Serializers; +namespace EntityDb.MongoDb.Documents.Serializers; internal class IdSerializer : IBsonSerializer { diff --git a/src/EntityDb.MongoDb/Serializers/TimeStampSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs similarity index 93% rename from src/EntityDb.MongoDb/Serializers/TimeStampSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs index def754e2..a9c1e48e 100644 --- a/src/EntityDb.MongoDb/Serializers/TimeStampSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.ValueObjects; using MongoDB.Bson.Serialization; -namespace EntityDb.MongoDb.Serializers; +namespace EntityDb.MongoDb.Documents.Serializers; internal class TimeStampSerializer : IBsonSerializer { diff --git a/src/EntityDb.MongoDb/Serializers/VersionNumberSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs similarity index 50% rename from src/EntityDb.MongoDb/Serializers/VersionNumberSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs index a3c635cc..c955da49 100644 --- a/src/EntityDb.MongoDb/Serializers/VersionNumberSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs @@ -1,39 +1,39 @@ -using EntityDb.Abstractions.ValueObjects; using MongoDB.Bson.Serialization; +using Version = EntityDb.Abstractions.ValueObjects.Version; -namespace EntityDb.MongoDb.Serializers; +namespace EntityDb.MongoDb.Documents.Serializers; -internal class VersionNumberSerializer : IBsonSerializer +internal class VersionSerializer : IBsonSerializer { - public Type ValueType => typeof(VersionNumber); + public Type ValueType => typeof(Version); object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { return Deserialize(context, args); } - public VersionNumber Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + public Version Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var longValue = context.Reader.ReadInt64(); var ulongValue = Convert.ToUInt64(longValue); - return new VersionNumber(ulongValue); + return new Version(ulongValue); } public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - if (value is not VersionNumber versionNumber) + if (value is not Version version) { throw new NotSupportedException(); } - Serialize(context, args, versionNumber); + Serialize(context, args, version); } - public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, VersionNumber versionNumber) + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Version version) { - var longValue = Convert.ToInt64(versionNumber.Value); + var longValue = Convert.ToInt64(version.Value); context.Writer.WriteInt64(longValue); } diff --git a/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs b/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs index 53f7ef50..51054ab5 100644 --- a/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs +++ b/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs @@ -1,14 +1,18 @@ using EntityDb.Abstractions.ValueObjects; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Documents; internal sealed record SnapshotDocument { + // ReSharper disable once InconsistentNaming + // ReSharper disable once UnusedMember.Global [BsonIgnoreIfNull] public ObjectId? _id { get; init; } - public string DataType { get; init; } = default!; - public BsonDocument Data { get; init; } = default!; - public Id PointerId { get; init; } - public VersionNumber PointerVersionNumber { get; init; } + public required string DataType { get; init; } + public required BsonDocument Data { get; init; } + public required Id SnapshotId { get; init; } + public required Version SnapshotVersion { get; init; } + public required Pointer SnapshotPointer { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDocument.cs index 11fb95b4..18daed8e 100644 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDocument.cs @@ -1,18 +1,17 @@ -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Common.Envelopes; -using EntityDb.Common.Queries; -using EntityDb.MongoDb.Commands; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Queries.FilterBuilders; -using EntityDb.MongoDb.Queries.SortBuilders; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.MongoDb.Documents.Commands; +using EntityDb.MongoDb.Documents.Queries; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Queries.FilterBuilders; +using EntityDb.MongoDb.Sources.Queries.SortBuilders; using MongoDB.Bson; namespace EntityDb.MongoDb.Documents; -internal sealed record TagDocument : DocumentBase, IEntityDocument +internal sealed record TagDocument : MessageDocumentBase { public const string CollectionName = "Tags"; @@ -20,29 +19,28 @@ internal sealed record TagDocument : DocumentBase, IEntityDocument private static readonly TagSortBuilder SortBuilder = new(); - public string Label { get; init; } = default!; - public string Value { get; init; } = default!; - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } + public required string Label { get; init; } + public required string Value { get; init; } public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, - ITransaction transaction, - ITransactionCommand transactionCommand + Source source, + Message message ) { - var tagDocuments = transactionCommand.AddTags + var tagDocuments = message.AddTags .Select(tag => new TagDocument { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + EntityId = message.EntityPointer.Id, + EntityVersion = message.EntityPointer.Version, + EntityPointer = message.EntityPointer, DataType = tag.GetType().Name, Data = envelopeService.Serialize(tag), Label = tag.Label, - Value = tag.Value + Value = tag.Value, }) .ToArray(); @@ -71,10 +69,10 @@ tagQuery.Options as MongoDbQueryOptions public static DeleteDocumentsCommand GetDeleteCommand ( - ITransactionCommand transactionCommand + Message message ) { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, transactionCommand.DeleteTags); + var deleteTagsQuery = new DeleteTagsQuery(message.EntityPointer.Id, message.DeleteTags); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj index 143f5b50..35f3e313 100644 --- a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj +++ b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj @@ -1,20 +1,16 @@  - EntityDb EventSourcing DDD CQRS MongoDb - An implementation of the EntityDb Transaction Repository interface, specifically for MongoDb. + Implementations of the EntityDb ISourceRepository and ISnapshotRepository interfaces, specifically for MongoDb. - + - + - - - \ No newline at end of file diff --git a/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs b/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs deleted file mode 100644 index e4690aeb..00000000 --- a/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs +++ /dev/null @@ -1,162 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Extensions; -using EntityDb.Common.Polyfills; -using EntityDb.MongoDb.Documents; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Transactions.Sessions; -using MongoDB.Bson; -using MongoDB.Driver; -using System.Runtime.CompilerServices; - -namespace EntityDb.MongoDb.Extensions; - -internal static class DocumentQueryExtensions -{ - private static readonly ProjectionDefinitionBuilder ProjectionBuilder = - Builders.Projection; - - private static readonly ProjectionDefinition NoDocumentIdProjection = - ProjectionBuilder.Exclude(nameof(DocumentBase._id)); - - private static readonly ProjectionDefinition EntityIdProjection = - ProjectionBuilder.Include(nameof(IEntityDocument.EntityId)); - - private static readonly ProjectionDefinition EntityIdsProjection = - ProjectionBuilder.Include(nameof(IEntitiesDocument.EntityIds)); - - private static readonly ProjectionDefinition TransactionIdProjection = - ProjectionBuilder.Include(nameof(ITransactionDocument.TransactionId)); - - private static readonly ProjectionDefinition DataProjection = - ProjectionBuilder.Combine - ( - ProjectionBuilder.Exclude(nameof(ITransactionDocument._id)), - ProjectionBuilder.Include(nameof(ITransactionDocument.Data)) - ); - - public static readonly ProjectionDefinition EntityVersionNumberProjection = - ProjectionBuilder.Combine - ( - ProjectionBuilder.Exclude(nameof(IEntityDocument._id)), - ProjectionBuilder.Include(nameof(IEntityDocument.EntityVersionNumber)) - ); - - private static IAsyncEnumerable EnumerateIds - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - ProjectionDefinition projection, - Func, IAsyncEnumerable> mapToIds, - CancellationToken cancellationToken - ) - { - var skip = documentQuery.Skip; - var limit = documentQuery.Limit; - - documentQuery = documentQuery with { Skip = null, Limit = null }; - - var documents = documentQuery.Execute(mongoSession, projection, cancellationToken); - - return documents.EnumerateIds(skip, limit, mapToIds); - } - - public static IAsyncEnumerable EnumerateTransactionIds - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - { - return documentQuery.EnumerateIds - ( - mongoSession, - TransactionIdProjection, - documents => documents.Select(document => document.TransactionId), - cancellationToken - ); - } - - public static IAsyncEnumerable EnumerateEntitiesIds - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - CancellationToken cancellationToken - ) - where TDocument : IEntitiesDocument - { - return documentQuery.EnumerateIds - ( - mongoSession, - EntityIdsProjection, - documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.EntityIds)), - cancellationToken - ); - } - - public static IAsyncEnumerable EnumerateEntityIds - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - CancellationToken cancellationToken - ) - where TDocument : IEntityDocument - { - return documentQuery.EnumerateIds - ( - mongoSession, - EntityIdProjection, - documents => documents.Select(document => document.EntityId), - cancellationToken - ); - } - - public static async IAsyncEnumerable EnumerateData - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - IEnvelopeService envelopeService, - [EnumeratorCancellation] CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - { - var documents = documentQuery.Execute(mongoSession, DataProjection, cancellationToken); - - await foreach (var document in documents) - { - yield return envelopeService.Deserialize(document.Data); - } - } - - public static IAsyncEnumerable> EnumerateEntityAnnotation - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - IEnvelopeService envelopeService, - CancellationToken cancellationToken - ) - where TDocument : IEntityDocument - where TData : notnull - { - var documents = documentQuery.Execute(mongoSession, NoDocumentIdProjection, cancellationToken); - - return documents.EnumerateEntityAnnotation(envelopeService, cancellationToken); - } - - public static IAsyncEnumerable> EnumerateEntitiesAnnotation - ( - this DocumentQuery documentQuery, - IMongoSession mongoSession, - IEnvelopeService envelopeService, - CancellationToken cancellationToken - ) - where TDocument : IEntitiesDocument - where TData : notnull - { - var documents = documentQuery.Execute(mongoSession, NoDocumentIdProjection, cancellationToken); - - return documents.EnumerateEntitiesAnnotation(envelopeService, cancellationToken); - } -} diff --git a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs index 3e4bc810..037ac709 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs @@ -14,16 +14,12 @@ private static readonly IndexKeysDefinitionBuilder private static readonly CreateIndexOptions UniquenessConstraint = new() { - Name = "Uniqueness Constraint", - Unique = true, + Name = "Uniqueness Constraint", Unique = true, }; - private static readonly CreateIndexOptions LookupIndex = new() - { - Name = "Lookup Index", - }; + private static readonly CreateIndexOptions LookupIndex = new() { Name = "Lookup Index" }; - private static readonly Dictionary[]> TransactionCollections = new() + private static readonly Dictionary[]> SourceCollections = new() { [AgentSignatureDocument.CollectionName] = new[] { @@ -31,22 +27,22 @@ private static readonly IndexKeysDefinitionBuilder ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(AgentSignatureDocument.TransactionId)) + IndexKeysBuilder.Descending(nameof(AgentSignatureDocument.SourceId)) ), UniquenessConstraint - ) + ), }, - [CommandDocument.CollectionName] = new[] + [DeltaDocument.CollectionName] = new[] { new CreateIndexModel ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(CommandDocument.EntityId)), - IndexKeysBuilder.Descending(nameof(CommandDocument.EntityVersionNumber)) + IndexKeysBuilder.Descending(nameof(DeltaDocument.EntityId)), + IndexKeysBuilder.Descending(nameof(DeltaDocument.EntityVersion)) ), UniquenessConstraint - ) + ), }, [LeaseDocument.CollectionName] = new[] { @@ -59,7 +55,7 @@ private static readonly IndexKeysDefinitionBuilder IndexKeysBuilder.Descending(nameof(LeaseDocument.Value)) ), UniquenessConstraint - ) + ), }, [TagDocument.CollectionName] = new[] { @@ -71,30 +67,22 @@ private static readonly IndexKeysDefinitionBuilder IndexKeysBuilder.Descending(nameof(TagDocument.Value)) ), LookupIndex - ) - } + ), + }, }; - private static readonly CreateIndexModel[] SnapshotCollection = new[] + private static readonly CreateIndexModel[] SnapshotCollection = { - new CreateIndexModel - ( + new( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(SnapshotDocument.PointerId)), - IndexKeysBuilder.Descending(nameof(SnapshotDocument.PointerVersionNumber)) + IndexKeysBuilder.Descending(nameof(SnapshotDocument.SnapshotId)), + IndexKeysBuilder.Descending(nameof(SnapshotDocument.SnapshotVersion)) ), UniquenessConstraint ), }; - [Obsolete("Please use ProvisionTransactionCollections instead. This will be removed in a later version.")] - public static Task ProvisionCollections(this IMongoClient mongoClient, string serviceName, - CancellationToken cancellationToken = default) - { - return ProvisionTransactionCollections(mongoClient, serviceName, cancellationToken); - } - /// /// Provisions the needed collections on the database. /// @@ -106,12 +94,12 @@ public static Task ProvisionCollections(this IMongoClient mongoClient, string se /// You should ONLY use this in your code for integration testing. Real databases should be provisioned using the /// dotnet tool EntityDb.MongoDb.Provisioner. /// - public static async Task ProvisionTransactionCollections(this IMongoClient mongoClient, string serviceName, + public static async Task ProvisionSourceCollections(this IMongoClient mongoClient, string serviceName, CancellationToken cancellationToken = default) { var mongoDatabase = mongoClient.GetDatabase(serviceName); - foreach (var (collectionName, collectionIndices) in TransactionCollections) + foreach (var (collectionName, collectionIndices) in SourceCollections) { var mongoCollection = mongoDatabase.GetCollection(collectionName); @@ -135,17 +123,19 @@ public static async Task ProvisionTransactionCollections(this IMongoClient mongo /// /// The mongo client. /// The name of the service, which is used as the name of the database. + /// The name of the collection /// A cancellation token. /// An asynchronous task that, when complete, signals that the collections have been provisioned. /// /// You should ONLY use this in your code for integration testing. Real databases should be provisioned using the /// dotnet tool EntityDb.MongoDb.Provisioner. /// - public static async Task ProvisionSnapshotCollection(this IMongoClient mongoClient, string serviceName, string collectionName, + public static async Task ProvisionSnapshotCollection(this IMongoClient mongoClient, string serviceName, + string collectionName, CancellationToken cancellationToken = default) { var collectionIndices = SnapshotCollection; - + var mongoDatabase = mongoClient.GetDatabase(serviceName); var mongoCollection = mongoDatabase.GetCollection(collectionName); diff --git a/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs deleted file mode 100644 index 67ef0f4c..00000000 --- a/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using EntityDb.MongoDb.Transactions; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.MongoDb.Extensions; - -internal static class MongoDbTransactionRepositoryFactoryExtensions -{ - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static IMongoDbTransactionRepositoryFactory UseTestMode( - this IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory, - bool testMode) - { - return testMode - ? new TestModeMongoDbTransactionRepositoryFactory(mongoDbTransactionRepositoryFactory) - : mongoDbTransactionRepositoryFactory; - } - - public static IMongoDbTransactionRepositoryFactory UseAutoProvision( - this IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory, - IServiceProvider serviceProvider, - bool autoProvision) - { - return autoProvision - ? AutoProvisionMongoDbTransactionRepositoryFactory.Create(serviceProvider, mongoDbTransactionRepositoryFactory) - : mongoDbTransactionRepositoryFactory; - } -} diff --git a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs index 097d64a9..b1aacb18 100644 --- a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -1,9 +1,9 @@ using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Common.Extensions; -using EntityDb.MongoDb.Envelopes; +using EntityDb.MongoDb.Documents.Envelopes; using EntityDb.MongoDb.Snapshots; -using EntityDb.MongoDb.Transactions; +using EntityDb.MongoDb.Sources; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.MongoDb.Extensions; @@ -21,34 +21,34 @@ internal static void AddBsonDocumentEnvelopeService(this IServiceCollection serv } /// - /// Adds a production-ready implementation of to a service + /// Adds a production-ready implementation of to a service /// collection. /// /// The service collection. /// Modifies the behavior of the repository to accomodate tests. /// Modifies the behavior of the repository to auto-provision collections. - public static void AddMongoDbTransactions(this IServiceCollection serviceCollection, + public static void AddMongoDbSources(this IServiceCollection serviceCollection, bool testMode = false, bool autoProvision = false) { serviceCollection.AddBsonDocumentEnvelopeService(true); - serviceCollection.Add + serviceCollection.Add ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient ); - serviceCollection.Add + serviceCollection.Add ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, serviceProvider => serviceProvider - .GetRequiredService() + .GetRequiredService() .UseTestMode(testMode) .UseAutoProvision(serviceProvider, autoProvision) ); } /// - /// Adds a production-ready implementation of to a service + /// Adds a production-ready implementation of to a service /// collection. /// /// The service collection. diff --git a/src/EntityDb.MongoDb/Queries/BuilderBase.cs b/src/EntityDb.MongoDb/Queries/BuilderBase.cs deleted file mode 100644 index fe05823d..00000000 --- a/src/EntityDb.MongoDb/Queries/BuilderBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using EntityDb.Common.Envelopes; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Queries; - -internal abstract class BuilderBase -{ - protected const string DataTypeNameFieldName = - $"{nameof(DocumentBase.Data)}.{nameof(Envelope.Headers)}.{EnvelopeHelper.Type}"; -} diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs deleted file mode 100644 index e535dee0..00000000 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs +++ /dev/null @@ -1,21 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.FilterBuilders; - -internal sealed class AgentSignatureFilterBuilder : FilterBuilderBase, - IAgentSignatureFilterBuilder> -{ - public FilterDefinition SubjectIdsIn(params Id[] subjectIds) - { - return AnyIn(nameof(AgentSignatureDocument.EntityIds), subjectIds); - } - - public FilterDefinition AgentSignatureTypeIn(params Type[] agentSignatureTypes) - { - return DataTypeIn(agentSignatureTypes); - } -} diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/CommandFilterBuilder.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/CommandFilterBuilder.cs deleted file mode 100644 index a0aba557..00000000 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/CommandFilterBuilder.cs +++ /dev/null @@ -1,31 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.FilterBuilders; - -internal sealed class CommandFilterBuilder : FilterBuilderBase, - ICommandFilterBuilder> -{ - public FilterDefinition EntityIdIn(params Id[] entityIds) - { - return In(nameof(CommandDocument.EntityId), entityIds); - } - - public FilterDefinition EntityVersionNumberGte(VersionNumber entityVersionNumber) - { - return Gte(nameof(CommandDocument.EntityVersionNumber), entityVersionNumber); - } - - public FilterDefinition EntityVersionNumberLte(VersionNumber entityVersionNumber) - { - return Lte(nameof(CommandDocument.EntityVersionNumber), entityVersionNumber); - } - - public FilterDefinition CommandTypeIn(params Type[] commandTypes) - { - return DataTypeIn(commandTypes); - } -} diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/LeaseFilterBuilder.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/LeaseFilterBuilder.cs deleted file mode 100644 index 7d3f15b5..00000000 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/LeaseFilterBuilder.cs +++ /dev/null @@ -1,45 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.FilterBuilders; - -internal sealed class LeaseFilterBuilder : FilterBuilderBase, ILeaseFilterBuilder> -{ - public FilterDefinition EntityIdIn(params Id[] entityIds) - { - return In(nameof(LeaseDocument.EntityId), entityIds); - } - - public FilterDefinition EntityVersionNumberGte(VersionNumber entityVersionNumber) - { - return Gte(nameof(LeaseDocument.EntityVersionNumber), entityVersionNumber); - } - - public FilterDefinition EntityVersionNumberLte(VersionNumber entityVersionNumber) - { - return Lte(nameof(LeaseDocument.EntityVersionNumber), entityVersionNumber); - } - - public FilterDefinition LeaseTypeIn(params Type[] leaseTypes) - { - return DataTypeIn(leaseTypes); - } - - public FilterDefinition LeaseScopeEq(string scope) - { - return Eq(nameof(LeaseDocument.Scope), scope); - } - - public FilterDefinition LeaseLabelEq(string label) - { - return Eq(nameof(LeaseDocument.Label), label); - } - - public FilterDefinition LeaseValueEq(string value) - { - return Eq(nameof(LeaseDocument.Value), value); - } -} diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/TagFilterBuilder.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/TagFilterBuilder.cs deleted file mode 100644 index d1dced25..00000000 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/TagFilterBuilder.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.FilterBuilders; - -internal sealed class TagFilterBuilder : FilterBuilderBase, ITagFilterBuilder> -{ - public FilterDefinition EntityIdIn(params Id[] entityIds) - { - return In(nameof(TagDocument.EntityId), entityIds); - } - - public FilterDefinition EntityVersionNumberGte(VersionNumber entityVersionNumber) - { - return Gte(nameof(TagDocument.EntityVersionNumber), entityVersionNumber); - } - - public FilterDefinition EntityVersionNumberLte(VersionNumber entityVersionNumber) - { - return Lte(nameof(TagDocument.EntityVersionNumber), entityVersionNumber); - } - - public FilterDefinition TagTypeIn(params Type[] tagTypes) - { - return DataTypeIn(tagTypes); - } - - public FilterDefinition TagLabelEq(string label) - { - return Eq(nameof(TagDocument.Label), label); - } - - public FilterDefinition TagValueEq(string value) - { - return Eq(nameof(TagDocument.Value), value); - } -} diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs deleted file mode 100644 index 1f48ab08..00000000 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.SortBuilders; - -internal sealed class AgentSignatureSortBuilder : SortBuilderBase, - IAgentSignatureSortBuilder> -{ - public SortDefinition EntityIds(bool ascending) - { - return Sort(ascending, nameof(AgentSignatureDocument.EntityIds)); - } - - public SortDefinition AgentSignatureType(bool ascending) - { - return SortDataType(ascending); - } -} diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/CommandSortBuilder.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/CommandSortBuilder.cs deleted file mode 100644 index 09ea7c43..00000000 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/CommandSortBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.SortBuilders; - -internal sealed class CommandSortBuilder : SortBuilderBase, ICommandSortBuilder> -{ - public SortDefinition EntityId(bool ascending) - { - return Sort(ascending, nameof(CommandDocument.EntityId)); - } - - public SortDefinition EntityVersionNumber(bool ascending) - { - return Sort(ascending, nameof(CommandDocument.EntityVersionNumber)); - } - - public SortDefinition CommandType(bool ascending) - { - return SortDataType(ascending); - } -} diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/LeaseSortBuilder.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/LeaseSortBuilder.cs deleted file mode 100644 index 06d56232..00000000 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/LeaseSortBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.SortBuilders; - -internal sealed class LeaseSortBuilder : SortBuilderBase, ILeaseSortBuilder> -{ - public SortDefinition EntityId(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.EntityId)); - } - - public SortDefinition EntityVersionNumber(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.EntityVersionNumber)); - } - - public SortDefinition LeaseType(bool ascending) - { - return SortDataType(ascending); - } - - public SortDefinition LeaseScope(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.Scope)); - } - - public SortDefinition LeaseLabel(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.Label)); - } - - public SortDefinition LeaseValue(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.Value)); - } -} diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/TagSortBuilder.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/TagSortBuilder.cs deleted file mode 100644 index 12c62d79..00000000 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/TagSortBuilder.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.SortBuilders; - -internal sealed class TagSortBuilder : SortBuilderBase, ITagSortBuilder> -{ - public SortDefinition EntityId(bool ascending) - { - return Sort(ascending, nameof(TagDocument.EntityId)); - } - - public SortDefinition EntityVersionNumber(bool ascending) - { - return Sort(ascending, nameof(TagDocument.EntityVersionNumber)); - } - - public SortDefinition TagType(bool ascending) - { - return SortDataType(ascending); - } - - public SortDefinition TagLabel(bool ascending) - { - return Sort(ascending, nameof(TagDocument.Label)); - } - - public SortDefinition TagValue(bool ascending) - { - return Sort(ascending, nameof(TagDocument.Value)); - } -} diff --git a/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs similarity index 92% rename from src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs rename to src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs index 7ff4664a..2a0c8ffc 100644 --- a/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs @@ -5,14 +5,15 @@ namespace EntityDb.MongoDb.Snapshots; -internal sealed class AutoProvisionMongoDbSnapshotRepositoryFactory : MongoDbSnapshotRepositoryFactoryWrapper +internal sealed class + AutoProvisionMongoDbSnapshotRepositoryFactory : MongoDbSnapshotRepositoryFactoryWrapper { // ReSharper disable once StaticMemberInGenericType private static readonly SemaphoreSlim Lock = new(1); - + // ReSharper disable once StaticMemberInGenericType private static bool _provisioned; - + private readonly ILogger> _logger; public AutoProvisionMongoDbSnapshotRepositoryFactory @@ -76,7 +77,8 @@ await mongoSession.MongoDatabase.Client.ProvisionSnapshotCollection public static IMongoDbSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory) { - return ActivatorUtilities.CreateInstance>(serviceProvider, + return ActivatorUtilities.CreateInstance>( + serviceProvider, mongoDbSnapshotRepositoryFactory); } } diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs index 8d8d9fb9..c96b50c6 100644 --- a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs +++ b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs @@ -35,8 +35,9 @@ await _mongoSession.Upsert(new SnapshotDocument { DataType = snapshot.GetType().Name, Data = _envelopeService.Serialize(snapshot), - PointerId = snapshotPointer.Id, - PointerVersionNumber = snapshotPointer.VersionNumber, + SnapshotId = snapshotPointer.Id, + SnapshotVersion = snapshotPointer.Version, + SnapshotPointer = snapshotPointer, }, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs index c58f50ce..d44bff21 100644 --- a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs @@ -3,7 +3,6 @@ using EntityDb.Common.Envelopes; using EntityDb.Common.Snapshots; using EntityDb.MongoDb.Snapshots.Sessions; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; diff --git a/src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs similarity index 82% rename from src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs rename to src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs index 46145d9c..7f1941ae 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoDbSnapshotRepositoryFactoryExtensions.cs +++ b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs @@ -1,7 +1,6 @@ -using EntityDb.MongoDb.Snapshots; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; -namespace EntityDb.MongoDb.Extensions; +namespace EntityDb.MongoDb.Snapshots; internal static class MongoDbSnapshotRepositoryFactoryExtensions { @@ -21,7 +20,8 @@ public static IMongoDbSnapshotRepositoryFactory UseAutoProvision.Create(serviceProvider, mongoDbSnapshotRepositoryFactory) + ? AutoProvisionMongoDbSnapshotRepositoryFactory.Create(serviceProvider, + mongoDbSnapshotRepositoryFactory) : mongoDbSnapshotRepositoryFactory; } } diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs index f7154d16..ffa8367d 100644 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs @@ -14,7 +14,7 @@ internal interface IMongoSession : IDisposableResource Task Find(Pointer snapshotPointer, CancellationToken cancellationToken); - Task Delete(Pointer[] snapshotPointers, CancellationToken cancellationToken); + Task Delete(Pointer[] snapshotPointer, CancellationToken cancellationToken); void StartTransaction(); Task CommitTransaction(CancellationToken cancellationToken); diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs index ff879e72..d4862d30 100644 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs @@ -1,17 +1,17 @@ using EntityDb.Abstractions.Snapshots; -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Sessions; using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; namespace EntityDb.MongoDb.Snapshots.Sessions; /// -/// Configuration options for the MongoDb implementation of . +/// Configuration options for the MongoDb implementation of . /// public sealed class MongoDbSnapshotSessionOptions { /// - /// A connection string that is compatible with + /// A connection string that is compatible with /// public string ConnectionString { get; set; } = default!; @@ -40,10 +40,10 @@ public sealed class MongoDbSnapshotSessionOptions /// public TimeSpan? WriteTimeout { get; set; } - /// + /// [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { - return $"{nameof(MongoDbTransactionSessionOptions)}"; + return $"{nameof(MongoDbSourceSessionOptions)}"; } } diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs index eee66539..5cebf7a3 100644 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs @@ -17,23 +17,13 @@ internal record MongoSession MongoDbSnapshotSessionOptions Options ) : DisposableResourceBaseRecord, IMongoSession { - public string CollectionName => Options.CollectionName; - private static readonly WriteConcern WriteConcern = WriteConcern.WMajority; private static readonly FilterDefinitionBuilder Filter = Builders.Filter; private static readonly ReplaceOptions UpsertOptions = new() { IsUpsert = true }; + public string CollectionName => Options.CollectionName; - private static FilterDefinition GetFilter(Id pointerId, VersionNumber pointerVersionNumber) - { - return Filter.And - ( - Filter.Eq(document => document.PointerId, pointerId), - Filter.Eq(document => document.PointerVersionNumber, pointerVersionNumber) - ); - } - public async Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken) { AssertNotReadOnly(); @@ -54,10 +44,10 @@ await MongoDatabase .ReplaceOneAsync ( ClientSessionHandle, - GetFilter(snapshotDocument.PointerId, snapshotDocument.PointerVersionNumber), + GetFilter(snapshotDocument.SnapshotPointer), snapshotDocument, UpsertOptions, - cancellationToken: cancellationToken + cancellationToken ); Logger @@ -76,7 +66,7 @@ public async Task Find CancellationToken cancellationToken ) { - var filter = GetFilter(snapshotPointer.Id, snapshotPointer.VersionNumber); + var filter = GetFilter(snapshotPointer); var find = MongoDatabase .GetCollection(Options.CollectionName) @@ -112,15 +102,15 @@ CancellationToken cancellationToken return snapshotDocument; } - - public async Task Delete(Pointer[] snapshotPointers, CancellationToken cancellationToken) + + public async Task Delete(Pointer[] snapshotPointer, CancellationToken cancellationToken) { AssertNotReadOnly(); - - var filter = Filter.And(snapshotPointers.Select(pointer => GetFilter(pointer.Id, pointer.VersionNumber))); + + var filter = Filter.And(snapshotPointer.Select(GetFilter)); var serverSessionId = ClientSessionHandle.ServerSession.Id.ToString(); - + var command = MongoDatabase .GetCollection(Options.CollectionName) .Find(filter) @@ -164,7 +154,7 @@ public IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options) public void StartTransaction() { AssertNotReadOnly(); - + ClientSessionHandle.StartTransaction(new TransactionOptions ( writeConcern: WriteConcern, @@ -219,6 +209,11 @@ public override ValueTask DisposeAsync() return base.DisposeAsync(); } + private static FilterDefinition GetFilter(Pointer snapshotPointer) + { + return Filter.Eq(document => document.SnapshotPointer, snapshotPointer); + } + private ReadPreference GetReadPreference() { if (!Options.ReadOnly) @@ -231,7 +226,7 @@ private ReadPreference GetReadPreference() : ReadPreference.PrimaryPreferred; } - [ExcludeFromCodeCoverage(Justification = "Tests should always run in a transaction.")] + [ExcludeFromCodeCoverage(Justification = "Tests should always run in a source.")] private ReadConcern GetReadConcern() { return ClientSessionHandle.IsInTransaction diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs index 3e46aee3..f9e5e7c4 100644 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs @@ -6,9 +6,9 @@ namespace EntityDb.MongoDb.Snapshots.Sessions; internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession -{ +{ public string CollectionName => MongoSession.CollectionName; - + public IMongoDatabase MongoDatabase => MongoSession.MongoDatabase; public Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken) @@ -25,14 +25,14 @@ public Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancella ); } - public Task Delete(Pointer[] snapshotPointers, CancellationToken cancellationToken) + public Task Delete(Pointer[] snapshotPointer, CancellationToken cancellationToken) { - return MongoSession.Delete(snapshotPointers, cancellationToken); + return MongoSession.Delete(snapshotPointer, cancellationToken); } public IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options) { - return this with { MongoSession = MongoSession.WithSessionOptions(options) }; + return new TestModeMongoSession(MongoSession.WithSessionOptions(options)); } public void StartTransaction() diff --git a/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs index 4bcf72b0..54a207d7 100644 --- a/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs @@ -25,8 +25,7 @@ public override async Task CreateSession(MongoDbSnapshotSessionOp var normalOptions = new MongoDbSnapshotSessionOptions { - ConnectionString = options.ConnectionString, - DatabaseName = options.DatabaseName, + ConnectionString = options.ConnectionString, DatabaseName = options.DatabaseName, }; var normalSession = await base.CreateSession(normalOptions, cancellationToken); @@ -42,7 +41,7 @@ public override async Task CreateSession(MongoDbSnapshotSessionOp } public override async ValueTask DisposeAsync() - { + { if (_sessions.HasValue) { await _sessions.Value.Normal.AbortTransaction(); diff --git a/src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/AutoProvisionMongoDbSourceRepositoryFactory.cs similarity index 58% rename from src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs rename to src/EntityDb.MongoDb/Sources/AutoProvisionMongoDbSourceRepositoryFactory.cs index 14857756..3a08b410 100644 --- a/src/EntityDb.MongoDb/Transactions/AutoProvisionMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/AutoProvisionMongoDbSourceRepositoryFactory.cs @@ -1,21 +1,20 @@ using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Sessions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace EntityDb.MongoDb.Transactions; +namespace EntityDb.MongoDb.Sources; -internal sealed class - AutoProvisionMongoDbTransactionRepositoryFactory : MongoDbTransactionRepositoryFactoryWrapper +internal sealed class AutoProvisionMongoDbSourceRepositoryFactory : MongoDbSourceRepositoryFactoryWrapper { private static readonly SemaphoreSlim Lock = new(1); private static bool _provisioned; - private readonly ILogger _logger; + private readonly ILogger _logger; - public AutoProvisionMongoDbTransactionRepositoryFactory( - ILogger logger, - IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory) : base( - mongoDbTransactionRepositoryFactory) + public AutoProvisionMongoDbSourceRepositoryFactory( + ILogger logger, + IMongoDbSourceRepositoryFactory mongoDbSourceRepositoryFactory) : base( + mongoDbSourceRepositoryFactory) { _logger = logger; } @@ -38,7 +37,7 @@ private void ReleaseLock() _logger.LogInformation("MongoDb Auto-Provisioning Lock Released"); } - public override async Task CreateSession(MongoDbTransactionSessionOptions options, + public override async Task CreateSession(MongoDbSourceSessionOptions options, CancellationToken cancellationToken) { var mongoSession = await base.CreateSession(options, cancellationToken); @@ -52,7 +51,8 @@ public override async Task CreateSession(MongoDbTransactionSessio return mongoSession; } - await mongoSession.MongoDatabase.Client.ProvisionTransactionCollections(mongoSession.MongoDatabase.DatabaseNamespace + await mongoSession.MongoDatabase.Client.ProvisionSourceCollections(mongoSession.MongoDatabase + .DatabaseNamespace .DatabaseName, cancellationToken); _provisioned = true; @@ -64,10 +64,10 @@ await mongoSession.MongoDatabase.Client.ProvisionTransactionCollections(mongoSes return mongoSession; } - public static IMongoDbTransactionRepositoryFactory Create(IServiceProvider serviceProvider, - IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory) + public static IMongoDbSourceRepositoryFactory Create(IServiceProvider serviceProvider, + IMongoDbSourceRepositoryFactory mongoDbSourceRepositoryFactory) { - return ActivatorUtilities.CreateInstance(serviceProvider, - mongoDbTransactionRepositoryFactory); + return ActivatorUtilities.CreateInstance(serviceProvider, + mongoDbSourceRepositoryFactory); } } diff --git a/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs new file mode 100644 index 00000000..17f27944 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.MongoDb.Sources.Sessions; + +namespace EntityDb.MongoDb.Sources; + +internal interface IMongoDbSourceRepositoryFactory : ISourceRepositoryFactory +{ + async Task ISourceRepositoryFactory.CreateRepository( + string sourceSessionOptionsName, CancellationToken cancellationToken) + { + var options = GetSourceSessionOptions(sourceSessionOptionsName); + + var mongoSession = await CreateSession(options, cancellationToken); + + return CreateRepository(mongoSession); + } + + MongoDbSourceSessionOptions GetSourceSessionOptions(string sourceSessionOptionsName); + + Task CreateSession(MongoDbSourceSessionOptions options, + CancellationToken cancellationToken); + + ISourceRepository CreateRepository(IMongoSession mongoSession); +} diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs new file mode 100644 index 00000000..ebc3e6c6 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -0,0 +1,240 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Exceptions; +using EntityDb.MongoDb.Documents; +using EntityDb.MongoDb.Documents.Queries; +using EntityDb.MongoDb.Sources.Sessions; +using MongoDB.Bson; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.MongoDb.Sources; + +internal class MongoDbSourceRepository : DisposableResourceBaseClass, ISourceRepository +{ + private readonly IEnvelopeService _envelopeService; + private readonly IMongoSession _mongoSession; + + public MongoDbSourceRepository + ( + IMongoSession mongoSession, + IEnvelopeService envelopeService + ) + { + _mongoSession = mongoSession; + _envelopeService = envelopeService; + } + + public IAsyncEnumerable EnumerateSourceIds(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(messageGroupQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateSourceIds(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return DeltaDocument + .GetQuery(messageQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default) + { + return LeaseDocument + .GetQuery(leaseQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, + CancellationToken cancellationToken = default) + { + return TagDocument + .GetQuery(tagQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateEntityPointers(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(messageGroupQuery) + .EnumerateMessageGroupEntityPointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateEntityPointers(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return DeltaDocument + .GetQuery(messageQuery) + .EnumerateMessageEntityPointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateEntityPointers(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default) + { + return LeaseDocument + .GetQuery(leaseQuery) + .EnumerateMessageEntityPointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateEntityPointers(ITagQuery tagQuery, + CancellationToken cancellationToken = default) + { + return TagDocument + .GetQuery(tagQuery) + .EnumerateMessageEntityPointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateAgentSignatures(IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(messageGroupQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable EnumerateDeltas(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return DeltaDocument + .GetQuery(messageQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, + CancellationToken cancellationToken = default) + { + return LeaseDocument + .GetQuery(leaseQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, + CancellationToken cancellationToken = default) + { + return TagDocument + .GetQuery(tagQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + IMessageGroupQuery messageGroupQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(messageGroupQuery) + .EnumerateEntitiesAnnotation(_mongoSession, _envelopeService, + cancellationToken); + } + + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageQuery messageQuery, + CancellationToken cancellationToken = default) + { + return DeltaDocument + .GetQuery(messageQuery) + .EnumerateEntityAnnotation(_mongoSession, _envelopeService, cancellationToken); + } + + public async Task Commit(Source source, + CancellationToken cancellationToken = default) + { + try + { + _mongoSession.StartTransaction(); + + await PutAgentSignature(source, cancellationToken); + + foreach (var message in source.Messages) + { + var currentMessage = message; + + cancellationToken.ThrowIfCancellationRequested(); + + var previousVersion = await DeltaDocument + .GetLastEntityVersion(_mongoSession, message.EntityPointer.Id, cancellationToken); + + if (message.EntityPointer.Version == Version.Zero) + { + currentMessage = currentMessage with + { + EntityPointer = currentMessage.EntityPointer.Id + previousVersion.Next(), + }; + } + else + { + OptimisticConcurrencyException.ThrowIfMismatch(previousVersion.Next(), + message.EntityPointer.Version); + } + + await Put(source, currentMessage, cancellationToken); + } + + cancellationToken.ThrowIfCancellationRequested(); + + await _mongoSession.CommitTransaction(cancellationToken); + + return true; + } + catch + { + await _mongoSession.AbortTransaction(); + + throw; + } + } + + public override async ValueTask DisposeAsync() + { + await _mongoSession.DisposeAsync(); + } + + private async Task PutAgentSignature(Source source, CancellationToken cancellationToken) + { + await AgentSignatureDocument + .GetInsertCommand(_envelopeService, source) + .Execute(_mongoSession, cancellationToken); + } + + private async Task Put(Source source, Message message, CancellationToken cancellationToken) + { + await DeltaDocument + .GetInsertCommand(_envelopeService, source, message) + .Execute(_mongoSession, cancellationToken); + + if (message.AddLeases.Length > 0) + { + await LeaseDocument + .GetInsertCommand(_envelopeService, source, message) + .Execute(_mongoSession, cancellationToken); + } + + if (message.AddTags.Length > 0) + { + await TagDocument + .GetInsertCommand(_envelopeService, source, message) + .Execute(_mongoSession, cancellationToken); + } + + if (message.DeleteLeases.Length > 0) + { + await LeaseDocument + .GetDeleteCommand(message) + .Execute(_mongoSession, cancellationToken); + } + + if (message.DeleteTags.Length > 0) + { + await TagDocument + .GetDeleteCommand(message) + .Execute(_mongoSession, cancellationToken); + } + } +} diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs similarity index 53% rename from src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs rename to src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs index 475fd1a9..9e7f7785 100644 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs @@ -1,24 +1,24 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; -using EntityDb.Common.Transactions; -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.Common.Sources; +using EntityDb.MongoDb.Sources.Sessions; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Transactions; +namespace EntityDb.MongoDb.Sources; -internal class MongoDbTransactionRepositoryFactory : DisposableResourceBaseClass, IMongoDbTransactionRepositoryFactory +internal class MongoDbSourceRepositoryFactory : DisposableResourceBaseClass, IMongoDbSourceRepositoryFactory { private readonly IEnvelopeService _envelopeService; - private readonly IOptionsFactory _optionsFactory; + private readonly IOptionsFactory _optionsFactory; private readonly IServiceProvider _serviceProvider; - public MongoDbTransactionRepositoryFactory + public MongoDbSourceRepositoryFactory ( IServiceProvider serviceProvider, - IOptionsFactory optionsFactory, + IOptionsFactory optionsFactory, IEnvelopeService envelopeService ) { @@ -27,12 +27,12 @@ IEnvelopeService envelopeService _envelopeService = envelopeService; } - public MongoDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName) + public MongoDbSourceSessionOptions GetSourceSessionOptions(string sourceSessionOptionsName) { - return _optionsFactory.Create(transactionSessionOptionsName); + return _optionsFactory.Create(sourceSessionOptionsName); } - public async Task CreateSession(MongoDbTransactionSessionOptions options, + public async Task CreateSession(MongoDbSourceSessionOptions options, CancellationToken cancellationToken) { var mongoClient = new MongoClient(options.ConnectionString); @@ -52,17 +52,17 @@ await mongoClient.StartSessionAsync(new ClientSessionOptions { CausalConsistency ); } - public ITransactionRepository CreateRepository + public ISourceRepository CreateRepository ( IMongoSession mongoSession ) { - var mongoDbTransactionRepository = new MongoDbTransactionRepository + var mongoDbSourceRepository = new MongoDbSourceRepository ( mongoSession, _envelopeService ); - return TryCatchTransactionRepository.Create(_serviceProvider, mongoDbTransactionRepository); + return TryCatchSourceRepository.Create(_serviceProvider, mongoDbSourceRepository); } } diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryExtensions.cs new file mode 100644 index 00000000..b672988f --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryExtensions.cs @@ -0,0 +1,26 @@ +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.MongoDb.Sources; + +internal static class MongoDbSourceRepositoryFactoryExtensions +{ + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static IMongoDbSourceRepositoryFactory UseTestMode( + this IMongoDbSourceRepositoryFactory mongoDbSourceRepositoryFactory, + bool testMode) + { + return testMode + ? new TestModeMongoDbSourceRepositoryFactory(mongoDbSourceRepositoryFactory) + : mongoDbSourceRepositoryFactory; + } + + public static IMongoDbSourceRepositoryFactory UseAutoProvision( + this IMongoDbSourceRepositoryFactory mongoDbSourceRepositoryFactory, + IServiceProvider serviceProvider, + bool autoProvision) + { + return autoProvision + ? AutoProvisionMongoDbSourceRepositoryFactory.Create(serviceProvider, mongoDbSourceRepositoryFactory) + : mongoDbSourceRepositoryFactory; + } +} diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryWrapper.cs new file mode 100644 index 00000000..1b49df00 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactoryWrapper.cs @@ -0,0 +1,41 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Disposables; +using EntityDb.MongoDb.Sources.Sessions; + +namespace EntityDb.MongoDb.Sources; + +internal abstract class MongoDbSourceRepositoryFactoryWrapper : DisposableResourceBaseClass, + IMongoDbSourceRepositoryFactory +{ + private readonly IMongoDbSourceRepositoryFactory _mongoDbSourceRepositoryFactory; + + protected MongoDbSourceRepositoryFactoryWrapper( + IMongoDbSourceRepositoryFactory mongoDbSourceRepositoryFactory) + { + _mongoDbSourceRepositoryFactory = mongoDbSourceRepositoryFactory; + } + + public virtual MongoDbSourceSessionOptions GetSourceSessionOptions(string sourceSessionOptionsName) + { + return _mongoDbSourceRepositoryFactory.GetSourceSessionOptions(sourceSessionOptionsName); + } + + public virtual Task CreateSession(MongoDbSourceSessionOptions options, + CancellationToken cancellationToken) + { + return _mongoDbSourceRepositoryFactory.CreateSession(options, cancellationToken); + } + + public virtual ISourceRepository CreateRepository + ( + IMongoSession mongoSession + ) + { + return _mongoDbSourceRepositoryFactory.CreateRepository(mongoSession); + } + + public override async ValueTask DisposeAsync() + { + await _mongoDbSourceRepositoryFactory.DisposeAsync(); + } +} diff --git a/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/FilterBuilderBase.cs similarity index 69% rename from src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs rename to src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/FilterBuilderBase.cs index 8790b051..90ef9cfb 100644 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/FilterBuilderBase.cs @@ -1,29 +1,36 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Envelopes; using EntityDb.MongoDb.Documents; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Queries.FilterBuilders; +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal abstract class FilterBuilderBase : BuilderBase, IFilterBuilder> +internal abstract class FilterBuilderBase : IFilterBuilder> { private static readonly FilterDefinitionBuilder FilterBuilder = Builders.Filter; public FilterDefinition SourceTimeStampGte(TimeStamp timeStamp) { - return Gte(nameof(DocumentBase.TransactionTimeStamp), timeStamp); + return Gte(nameof(DocumentBase.SourceTimeStamp), timeStamp); } public FilterDefinition SourceTimeStampLte(TimeStamp timeStamp) { - return Lte(nameof(DocumentBase.TransactionTimeStamp), timeStamp); + return Lte(nameof(DocumentBase.SourceTimeStamp), timeStamp); } - public FilterDefinition SourceIdIn(params Id[] transactionIds) + public FilterDefinition SourceIdIn(params Id[] sourceIds) { - return In(nameof(DocumentBase.TransactionId), transactionIds); + return In(nameof(DocumentBase.SourceId), sourceIds); + } + + public FilterDefinition DataTypeIn(params Type[] dataTypes) + { + var typeNames = dataTypes.GetTypeHeaderValues(); + + return FilterBuilder.In(nameof(DocumentBase.DataType), typeNames); } public FilterDefinition Not(FilterDefinition filter) @@ -51,9 +58,10 @@ protected static FilterDefinition In(string fieldName, IEn return FilterBuilder.In(fieldName, values); } - protected static FilterDefinition AnyIn(string fieldName, IEnumerable values) + protected static FilterDefinition AnyIn(string fieldName, + IEnumerable values) { - return FilterBuilder.In(fieldName, values); + return FilterBuilder.AnyIn(fieldName, values); } protected static FilterDefinition Gte(string fieldName, TValue value) @@ -65,11 +73,4 @@ protected static FilterDefinition Lte(string fieldName, TV { return FilterBuilder.Lte(fieldName, value); } - - protected static FilterDefinition DataTypeIn(params Type[] dataTypes) - { - var typeNames = dataTypes.GetTypeHeaderValues(); - - return FilterBuilder.In(DataTypeNameFieldName, typeNames); - } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs new file mode 100644 index 00000000..ec87730b --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs @@ -0,0 +1,25 @@ +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal sealed class LeaseFilterBuilder : MessageFilterBuilder, + ILeaseFilterBuilder> +{ + public FilterDefinition LeaseScopeEq(string scope) + { + return Eq(nameof(LeaseDocument.Scope), scope); + } + + public FilterDefinition LeaseLabelEq(string label) + { + return Eq(nameof(LeaseDocument.Label), label); + } + + public FilterDefinition LeaseValueEq(string value) + { + return Eq(nameof(LeaseDocument.Value), value); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs new file mode 100644 index 00000000..89227f26 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal class MessageFilterBuilder : FilterBuilderBase, + IMessageFilterBuilder> +{ + public FilterDefinition EntityIdIn(params Id[] entityIds) + { + return Or + ( + entityIds + .Select(entityId => Eq(nameof(MessageDocumentBase.EntityId), entityId)) + .ToArray() + ); + } + + public FilterDefinition EntityVersionGte(Version entityVersion) + { + return Gte(nameof(MessageDocumentBase.EntityVersion), entityVersion); + } + + public FilterDefinition EntityVersionLte(Version entityVersion) + { + return Lte(nameof(MessageDocumentBase.EntityVersion), entityVersion); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs new file mode 100644 index 00000000..56ad7bae --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal sealed class MessageGroupFilterBuilder : FilterBuilderBase, + IMessageGroupFilterBuilder> +{ + public FilterDefinition AnyEntityIdIn(params Id[] entityIds) + { + return AnyIn + ( + nameof(MessageGroupDocumentBase.EntityIds), + entityIds + ); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs new file mode 100644 index 00000000..d197975d --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal sealed class TagFilterBuilder : MessageFilterBuilder, + ITagFilterBuilder> +{ + public FilterDefinition TagLabelEq(string label) + { + return Eq(nameof(TagDocument.Label), label); + } + + public FilterDefinition TagValueEq(string value) + { + return Eq(nameof(TagDocument.Value), value); + } +} diff --git a/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs b/src/EntityDb.MongoDb/Sources/Queries/MongoDbQueryOptions.cs similarity index 86% rename from src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs rename to src/EntityDb.MongoDb/Sources/Queries/MongoDbQueryOptions.cs index c38adb46..f8d5e3b3 100644 --- a/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/MongoDbQueryOptions.cs @@ -1,6 +1,6 @@ using MongoDB.Driver; -namespace EntityDb.MongoDb.Queries; +namespace EntityDb.MongoDb.Sources.Queries; /// /// Options for configuring queries in MongoDb. diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs new file mode 100644 index 00000000..2e607b18 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; + +internal sealed class LeaseSortBuilder : MessageSortBuilder, ILeaseSortBuilder> +{ + public SortDefinition LeaseScope(bool ascending) + { + return Sort(ascending, nameof(LeaseDocument.Scope)); + } + + public SortDefinition LeaseLabel(bool ascending) + { + return Sort(ascending, nameof(LeaseDocument.Label)); + } + + public SortDefinition LeaseValue(bool ascending) + { + return Sort(ascending, nameof(LeaseDocument.Value)); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs new file mode 100644 index 00000000..b02557c3 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; + +internal sealed class MessageGroupSortBuilder : SortBuilderBase, + IMessageGroupSortBuilder> +{ + public SortDefinition EntityIds(bool ascending) + { + return Sort(ascending, nameof(AgentSignatureDocument.EntityIds)); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs new file mode 100644 index 00000000..01b99a7a --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; + +internal class MessageSortBuilder : SortBuilderBase, + IMessageSortBuilder> +{ + public SortDefinition EntityId(bool ascending) + { + return Sort(ascending, nameof(LeaseDocument.EntityId)); + } + + public SortDefinition EntityVersion(bool ascending) + { + return Sort(ascending, nameof(LeaseDocument.EntityVersion)); + } +} diff --git a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SortBuilderBase.cs similarity index 58% rename from src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs rename to src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SortBuilderBase.cs index 4e67025d..d8fe17fc 100644 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SortBuilderBase.cs @@ -1,22 +1,27 @@ -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.MongoDb.Documents; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Queries.SortBuilders; +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal abstract class SortBuilderBase : BuilderBase, ISortBuilder> +internal abstract class SortBuilderBase : ISortBuilder> { private static readonly SortDefinitionBuilder SortBuilder = Builders.Sort; public SortDefinition SourceTimeStamp(bool ascending) { - return Sort(ascending, nameof(DocumentBase.TransactionTimeStamp)); + return Sort(ascending, nameof(DocumentBase.SourceTimeStamp)); } public SortDefinition SourceId(bool ascending) { - return Sort(ascending, nameof(DocumentBase.TransactionId)); + return Sort(ascending, nameof(DocumentBase.SourceId)); + } + + public SortDefinition DataType(bool ascending) + { + return Sort(ascending, nameof(DocumentBase.DataType)); } public SortDefinition Combine(params SortDefinition[] sorts) @@ -30,9 +35,4 @@ protected static SortDefinition Sort(bool ascending, string fieldN ? SortBuilder.Ascending(fieldName) : SortBuilder.Descending(fieldName); } - - protected static SortDefinition SortDataType(bool ascending) - { - return Sort(ascending, DataTypeNameFieldName); - } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs new file mode 100644 index 00000000..4cbae5a7 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs @@ -0,0 +1,19 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; + +internal sealed class TagSortBuilder : MessageSortBuilder, ITagSortBuilder> +{ + public SortDefinition TagLabel(bool ascending) + { + return Sort(ascending, nameof(TagDocument.Label)); + } + + public SortDefinition TagValue(bool ascending) + { + return Sort(ascending, nameof(TagDocument.Value)); + } +} diff --git a/src/EntityDb.MongoDb/Transactions/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/IMongoSession.cs similarity index 82% rename from src/EntityDb.MongoDb/Transactions/Sessions/IMongoSession.cs rename to src/EntityDb.MongoDb/Sources/Sessions/IMongoSession.cs index 3280bb92..80bee7f2 100644 --- a/src/EntityDb.MongoDb/Transactions/Sessions/IMongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/IMongoSession.cs @@ -1,9 +1,9 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.MongoDb.Queries; +using EntityDb.MongoDb.Sources.Queries; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Transactions.Sessions; +namespace EntityDb.MongoDb.Sources.Sessions; internal interface IMongoSession : IDisposableResource { @@ -31,5 +31,5 @@ Task Delete(string collectionName, Task CommitTransaction(CancellationToken cancellationToken); Task AbortTransaction(); - IMongoSession WithTransactionSessionOptions(MongoDbTransactionSessionOptions options); + IMongoSession WithSourceSessionOptions(MongoDbSourceSessionOptions options); } diff --git a/src/EntityDb.MongoDb/Transactions/Sessions/MongoDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs similarity index 77% rename from src/EntityDb.MongoDb/Transactions/Sessions/MongoDbTransactionSessionOptions.cs rename to src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs index 67a53174..4a73c490 100644 --- a/src/EntityDb.MongoDb/Transactions/Sessions/MongoDbTransactionSessionOptions.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs @@ -1,21 +1,21 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.MongoDb.Transactions.Sessions; +namespace EntityDb.MongoDb.Sources.Sessions; /// -/// Configuration options for the MongoDb implementation of . +/// Configuration options for the MongoDb implementation of /// -public sealed class MongoDbTransactionSessionOptions +public sealed class MongoDbSourceSessionOptions { /// - /// A connection string that is compatible with + /// A connection string that is compatible with /// public string ConnectionString { get; set; } = default!; /// - /// The name of the database that contains the collections (AgentSignatures, Commands, Tags, Leases) + /// The name of the database that contains the collections (AgentSignatures, Deltas, Tags, Leases, Aliases) /// public string DatabaseName { get; set; } = default!; @@ -34,10 +34,10 @@ public sealed class MongoDbTransactionSessionOptions /// public TimeSpan? WriteTimeout { get; set; } - /// + /// [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { - return $"{nameof(MongoDbTransactionSessionOptions)}"; + return $"{nameof(MongoDbSourceSessionOptions)}"; } } diff --git a/src/EntityDb.MongoDb/Transactions/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs similarity index 92% rename from src/EntityDb.MongoDb/Transactions/Sessions/MongoSession.cs rename to src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs index 78cf6d4d..4eef75a9 100644 --- a/src/EntityDb.MongoDb/Transactions/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs @@ -1,6 +1,6 @@ using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; -using EntityDb.MongoDb.Queries; +using EntityDb.MongoDb.Sources.Queries; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MongoDB.Bson; @@ -8,14 +8,14 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace EntityDb.MongoDb.Transactions.Sessions; +namespace EntityDb.MongoDb.Sources.Sessions; internal record MongoSession ( ILogger Logger, IMongoDatabase MongoDatabase, IClientSessionHandle ClientSessionHandle, - MongoDbTransactionSessionOptions Options + MongoDbSourceSessionOptions Options ) : DisposableResourceBaseRecord, IMongoSession { private static readonly WriteConcern WriteConcern = WriteConcern.WMajority; @@ -30,7 +30,7 @@ public async Task Insert(string collectionName, TDocument[] bsonDocum Logger .LogInformation ( - "Started Running MongoDb Insert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}\n\nDocuments Inserted: {DocumentsInserted}", + "Started Running MongoDb Insert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}\n\nDocuments Committed: {DocumentsCommitted}", MongoDatabase.DatabaseNamespace, collectionName, serverSessionId, @@ -49,7 +49,7 @@ await MongoDatabase Logger .LogInformation ( - "Finished Running MongoDb Insert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}\n\nDocuments Inserted: {DocumentsInserted}", + "Finished Running MongoDb Insert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}\n\nDocuments Committed: {DocumentsCommitted}", MongoDatabase.DatabaseNamespace, collectionName, serverSessionId, @@ -131,7 +131,6 @@ [EnumeratorCancellation] CancellationToken cancellationToken } - public async Task Delete(string collectionName, FilterDefinition filterDefinition, CancellationToken cancellationToken) { @@ -172,7 +171,7 @@ public async Task Delete(string collectionName, ); } - public IMongoSession WithTransactionSessionOptions(MongoDbTransactionSessionOptions options) + public IMongoSession WithSourceSessionOptions(MongoDbSourceSessionOptions options) { return this with { Options = options }; } @@ -180,7 +179,7 @@ public IMongoSession WithTransactionSessionOptions(MongoDbTransactionSessionOpti public void StartTransaction() { AssertNotReadOnly(); - + ClientSessionHandle.StartTransaction(new TransactionOptions ( writeConcern: WriteConcern, @@ -247,7 +246,7 @@ private ReadPreference GetReadPreference() : ReadPreference.PrimaryPreferred; } - [ExcludeFromCodeCoverage(Justification = "Tests should always run in a transaction.")] + [ExcludeFromCodeCoverage(Justification = "Tests should always run in a source.")] private ReadConcern GetReadConcern() { return ClientSessionHandle.IsInTransaction @@ -268,7 +267,7 @@ public static IMongoSession Create IServiceProvider serviceProvider, IMongoDatabase mongoDatabase, IClientSessionHandle clientSessionHandle, - MongoDbTransactionSessionOptions options + MongoDbSourceSessionOptions options ) { return ActivatorUtilities.CreateInstance(serviceProvider, mongoDatabase, clientSessionHandle, diff --git a/src/EntityDb.MongoDb/Transactions/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs similarity index 84% rename from src/EntityDb.MongoDb/Transactions/Sessions/TestModeMongoSession.cs rename to src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs index c3bdba04..6b1e7779 100644 --- a/src/EntityDb.MongoDb/Transactions/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs @@ -1,9 +1,9 @@ using EntityDb.Common.Disposables; -using EntityDb.MongoDb.Queries; +using EntityDb.MongoDb.Sources.Queries; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Transactions.Sessions; +namespace EntityDb.MongoDb.Sources.Sessions; internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession { @@ -45,9 +45,9 @@ public Task Delete(string collectionName, return MongoSession.Delete(collectionName, filterDefinition, cancellationToken); } - public IMongoSession WithTransactionSessionOptions(MongoDbTransactionSessionOptions options) + public IMongoSession WithSourceSessionOptions(MongoDbSourceSessionOptions options) { - return this with { MongoSession = MongoSession.WithTransactionSessionOptions(options) }; + return new TestModeMongoSession(MongoSession.WithSourceSessionOptions(options)); } public void StartTransaction() diff --git a/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs similarity index 54% rename from src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs rename to src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs index b1088851..3f5c684b 100644 --- a/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs @@ -1,31 +1,30 @@ -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Sessions; -namespace EntityDb.MongoDb.Transactions; +namespace EntityDb.MongoDb.Sources; internal class - TestModeMongoDbTransactionRepositoryFactory : MongoDbTransactionRepositoryFactoryWrapper + TestModeMongoDbSourceRepositoryFactory : MongoDbSourceRepositoryFactoryWrapper { private (IMongoSession Normal, TestModeMongoSession TestMode)? _sessions; - public TestModeMongoDbTransactionRepositoryFactory( - IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory) : base( - mongoDbTransactionRepositoryFactory) + public TestModeMongoDbSourceRepositoryFactory( + IMongoDbSourceRepositoryFactory mongoDbSourceRepositoryFactory) : base( + mongoDbSourceRepositoryFactory) { } - public override async Task CreateSession(MongoDbTransactionSessionOptions options, + public override async Task CreateSession(MongoDbSourceSessionOptions options, CancellationToken cancellationToken) { if (_sessions.HasValue) { return _sessions.Value.TestMode - .WithTransactionSessionOptions(options); + .WithSourceSessionOptions(options); } - var normalOptions = new MongoDbTransactionSessionOptions + var normalOptions = new MongoDbSourceSessionOptions { - ConnectionString = options.ConnectionString, - DatabaseName = options.DatabaseName, + ConnectionString = options.ConnectionString, DatabaseName = options.DatabaseName, }; var normalSession = await base.CreateSession(normalOptions, cancellationToken); @@ -37,7 +36,7 @@ public override async Task CreateSession(MongoDbTransactionSessio _sessions = (normalSession, testModeSession); return _sessions.Value.TestMode - .WithTransactionSessionOptions(options); + .WithSourceSessionOptions(options); } public override async ValueTask DisposeAsync() diff --git a/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs deleted file mode 100644 index 144a7f35..00000000 --- a/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.MongoDb.Transactions.Sessions; - -namespace EntityDb.MongoDb.Transactions; - -internal interface IMongoDbTransactionRepositoryFactory : ITransactionRepositoryFactory -{ - async Task ITransactionRepositoryFactory.CreateRepository( - string transactionSessionOptionsName, CancellationToken cancellationToken) - { - var options = GetTransactionSessionOptions(transactionSessionOptionsName); - - var mongoSession = await CreateSession(options, cancellationToken); - - return CreateRepository(mongoSession); - } - - MongoDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName); - - Task CreateSession(MongoDbTransactionSessionOptions options, - CancellationToken cancellationToken); - - ITransactionRepository CreateRepository(IMongoSession mongoSession); -} diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs deleted file mode 100644 index fb36ccc6..00000000 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs +++ /dev/null @@ -1,229 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Exceptions; -using EntityDb.MongoDb.Documents; -using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Transactions.Sessions; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Transactions; - -internal class MongoDbTransactionRepository : DisposableResourceBaseClass, ITransactionRepository -{ - private readonly IEnvelopeService _envelopeService; - private readonly IMongoSession _mongoSession; - - public MongoDbTransactionRepository - ( - IMongoSession mongoSession, - IEnvelopeService envelopeService - ) - { - _mongoSession = mongoSession; - _envelopeService = envelopeService; - } - - public IAsyncEnumerable EnumerateTransactionIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateTransactionIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateTransactionIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateTransactionIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateTransactionIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return LeaseDocument - .GetQuery(leaseQuery) - .EnumerateTransactionIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateTransactionIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return TagDocument - .GetQuery(tagQuery) - .EnumerateTransactionIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateEntitiesIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateEntityIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return LeaseDocument - .GetQuery(leaseQuery) - .EnumerateEntityIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return TagDocument - .GetQuery(tagQuery) - .EnumerateEntityIds(_mongoSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable EnumerateCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return LeaseDocument - .GetQuery(leaseQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return TagDocument - .GetQuery(tagQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateEntitiesAnnotation(_mongoSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateEntityAnnotation(_mongoSession, _envelopeService, cancellationToken); - } - - public async Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default) - { - try - { - _mongoSession.StartTransaction(); - - await PutAgentSignature(transaction, cancellationToken); - - foreach (var transactionCommand in transaction.Commands) - { - cancellationToken.ThrowIfCancellationRequested(); - - VersionZeroReservedException.ThrowIfZero(transactionCommand.EntityVersionNumber); - - var previousVersionNumber = await CommandDocument - .GetLastEntityVersionNumber(_mongoSession, transactionCommand.EntityId, cancellationToken); - - OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber.Next(), - transactionCommand.EntityVersionNumber); - - await PutCommand(transaction, transactionCommand, cancellationToken); - } - - cancellationToken.ThrowIfCancellationRequested(); - - await _mongoSession.CommitTransaction(cancellationToken); - - return true; - } - catch - { - await _mongoSession.AbortTransaction(); - - throw; - } - } - - public override async ValueTask DisposeAsync() - { - await _mongoSession.DisposeAsync(); - } - - private async Task PutAgentSignature(ITransaction transaction, CancellationToken cancellationToken) - { - await AgentSignatureDocument - .GetInsertCommand(_envelopeService, transaction) - .Execute(_mongoSession, cancellationToken); - } - - private async Task PutCommand(ITransaction transaction, ITransactionCommand transactionCommand, - CancellationToken cancellationToken) - { - await CommandDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand) - .Execute(_mongoSession, cancellationToken); - - if (transactionCommand.AddLeases.Length > 0) - { - await LeaseDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand) - .Execute(_mongoSession, cancellationToken); - } - - if (transactionCommand.AddTags.Length > 0) - { - await TagDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand) - .Execute(_mongoSession, cancellationToken); - } - - if (transactionCommand.DeleteLeases.Length > 0) - { - await LeaseDocument - .GetDeleteCommand(transactionCommand) - .Execute(_mongoSession, cancellationToken); - } - - if (transactionCommand.DeleteTags.Length > 0) - { - await TagDocument - .GetDeleteCommand(transactionCommand) - .Execute(_mongoSession, cancellationToken); - } - } -} diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs deleted file mode 100644 index a847c4fb..00000000 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs +++ /dev/null @@ -1,41 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Disposables; -using EntityDb.MongoDb.Transactions.Sessions; - -namespace EntityDb.MongoDb.Transactions; - -internal abstract class MongoDbTransactionRepositoryFactoryWrapper : DisposableResourceBaseClass, - IMongoDbTransactionRepositoryFactory -{ - private readonly IMongoDbTransactionRepositoryFactory _mongoDbTransactionRepositoryFactory; - - protected MongoDbTransactionRepositoryFactoryWrapper( - IMongoDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory) - { - _mongoDbTransactionRepositoryFactory = mongoDbTransactionRepositoryFactory; - } - - public virtual MongoDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName) - { - return _mongoDbTransactionRepositoryFactory.GetTransactionSessionOptions(transactionSessionOptionsName); - } - - public virtual Task CreateSession(MongoDbTransactionSessionOptions options, - CancellationToken cancellationToken) - { - return _mongoDbTransactionRepositoryFactory.CreateSession(options, cancellationToken); - } - - public virtual ITransactionRepository CreateRepository - ( - IMongoSession mongoSession - ) - { - return _mongoDbTransactionRepositoryFactory.CreateRepository(mongoSession); - } - - public override async ValueTask DisposeAsync() - { - await _mongoDbTransactionRepositoryFactory.DisposeAsync(); - } -} diff --git a/src/EntityDb.Mvc/Agents/HttpContextAgentAccessor.cs b/src/EntityDb.Mvc/Agents/HttpContextAgentAccessor.cs index 77adf804..58d9feec 100644 --- a/src/EntityDb.Mvc/Agents/HttpContextAgentAccessor.cs +++ b/src/EntityDb.Mvc/Agents/HttpContextAgentAccessor.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Common.Agents; +using EntityDb.Abstractions.Sources.Agents; using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Agents; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -26,7 +26,7 @@ public HttpContextAgentAccessor _agentSignatureAugmenter = agentSignatureAugmenter; } - public async Task GetAgentAsync(string signatureOptionsName, CancellationToken cancellationToken) + public async Task GetAgent(string signatureOptionsName, CancellationToken cancellationToken) { var httpContext = _httpContextAccessor.HttpContext; @@ -39,7 +39,7 @@ public async Task GetAgentAsync(string signatureOptionsName, Cancellatio if (_agentSignatureAugmenter != null) { - applicationInfo = await _agentSignatureAugmenter.GetApplicationInfoAsync(cancellationToken); + applicationInfo = await _agentSignatureAugmenter.GetApplicationInfo(cancellationToken); } var signatureOptions = _httpContextAgentOptionsFactory.Create(signatureOptionsName); diff --git a/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs b/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs index daee8ba7..6aa3f985 100644 --- a/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs +++ b/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs @@ -4,11 +4,26 @@ namespace EntityDb.Mvc.Agents; /// -/// Represents the description of an agent who requests transactions using an +/// Represents the description of an agent who records sources using an /// . /// -public static class HttpContextAgentSignature +public class HttpContextAgentSignature { + /// + /// Request details + /// + public required RequestSnapshot Request { get; init; } + + /// + /// Connection details + /// + public required ConnectionSnapshot Connection { get; init; } + + /// + /// Application details + /// + public required Dictionary ApplicationInfo { get; init; } + private static NameValuesPairSnapshot[] GetNameValuesPairSnapshots( IEnumerable> dictionary, string[] redactedKeys, string redactedValue) { @@ -50,28 +65,32 @@ private static ConnectionSnapshot GetConnectionSnapshot(ConnectionInfo connectio ); } - internal static Snapshot GetSnapshot + internal static HttpContextAgentSignature GetSnapshot ( HttpContext httpContext, HttpContextAgentSignatureOptions httpContextAgentOptions, Dictionary applicationInfo ) { - return new Snapshot - ( - GetRequestSnapshot(httpContext.Request, httpContextAgentOptions), - GetConnectionSnapshot(httpContext.Connection), - applicationInfo - ); + return new HttpContextAgentSignature + { + Request = GetRequestSnapshot(httpContext.Request, httpContextAgentOptions), + Connection = GetConnectionSnapshot(httpContext.Connection), + ApplicationInfo = applicationInfo, + }; } + /// - /// Represents the headers used by agent. + /// Represents the connection used by agent. /// - public sealed record NameValuesPairSnapshot + public sealed record ConnectionSnapshot ( - string Name, - string?[] Values + string ConnectionId, + string? RemoteIpAddress, + int RemotePort, + string? LocalIpAddress, + int LocalPort ); /// @@ -89,24 +108,11 @@ NameValuesPairSnapshot[] QueryStringParams ); /// - /// Represents the connection used by agent. - /// - public sealed record ConnectionSnapshot - ( - string ConnectionId, - string? RemoteIpAddress, - int RemotePort, - string? LocalIpAddress, - int LocalPort - ); - - /// - /// Represents the signature of the agent. + /// Represents the headers used by agent. /// - public sealed record Snapshot + public sealed record NameValuesPairSnapshot ( - RequestSnapshot Request, - ConnectionSnapshot Connection, - Dictionary ApplicationInfo + string Name, + string?[] Values ); } diff --git a/src/EntityDb.Mvc/EntityDb.Mvc.csproj b/src/EntityDb.Mvc/EntityDb.Mvc.csproj index fe0bc4f0..c6a9864f 100644 --- a/src/EntityDb.Mvc/EntityDb.Mvc.csproj +++ b/src/EntityDb.Mvc/EntityDb.Mvc.csproj @@ -3,7 +3,7 @@ EntityDb EventSourcing DDD CQRS MVC - Provides a model for a Transaction Source that is mapped from an HttpContext. The source includes: Claims, Connection Information, and Raw Headers. + Provides a model for an AgentSignature that is mapped from an HttpContext. The source includes: Claims, Connection Information, and Raw Headers. diff --git a/src/EntityDb.Mvc/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Mvc/Extensions/ServiceCollectionExtensions.cs index f553d218..106d2730 100644 --- a/src/EntityDb.Mvc/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Mvc/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; using EntityDb.Common.Extensions; using EntityDb.Mvc.Agents; using Microsoft.Extensions.DependencyInjection; diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs index cbf8ecf9..e6e3c729 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs @@ -7,16 +7,6 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; internal class CreateCollections : CommandBase { - public record Arguments - ( - string GroupName, - string PublicKey, - string PrivateKey, - string ServiceName, - string ServicePassword, - string ClusterName - ); - private static async Task Execute(Arguments arguments) { const string expectedProtocol = "mongodb+srv://"; @@ -39,14 +29,14 @@ private static async Task Execute(Arguments arguments) new MongoClient( $"{expectedProtocol}{arguments.ServiceName}:{arguments.ServicePassword}@{cluster.SrvAddress[expectedProtocol.Length..]}/admin"); - await mongoClient.ProvisionTransactionCollections(arguments.ServiceName); + await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } protected static void AddClusterNameArgument(Command command) { var clusterNameArgument = new Argument("cluster-name") { - Description = "The name of the Cluster on which the entity will be provisioned." + Description = "The name of the Cluster on which the entity will be provisioned.", }; command.AddArgument(clusterNameArgument); @@ -56,7 +46,7 @@ public static void AddTo(Command parentCommand) { var createCollections = new Command("create-collections") { - Handler = CommandHandler.Create(Execute) + Handler = CommandHandler.Create(Execute), }; AddMongoDbAtlasArguments(createCollections); @@ -66,4 +56,14 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createCollections); } + + public record Arguments + ( + string GroupName, + string PublicKey, + string PrivateKey, + string ServiceName, + string ServicePassword, + string ClusterName + ); } diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs index efd13c44..79265ee6 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs @@ -7,14 +7,6 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; internal class CreateRole : CommandBase { - public record Arguments - ( - string GroupName, - string PublicKey, - string PrivateKey, - string ServiceName - ); - private static async Task Execute(Arguments arguments) { using var mongoDbAtlasClient = await GetMongoDbAtlasClient @@ -29,100 +21,50 @@ private static async Task Execute(Arguments arguments) return; } - var allClusterResources = new MongoDbAtlasResource - { - Cluster = true - }; + var allClusterResources = new MongoDbAtlasResource { Cluster = true }; - var allDbResources = new MongoDbAtlasResource - { - Db = arguments.ServiceName - }; + var allDbResources = new MongoDbAtlasResource { Db = arguments.ServiceName }; var agentSignatureResource = new MongoDbAtlasResource { - Db = arguments.ServiceName, - Collection = AgentSignatureDocument.CollectionName + Db = arguments.ServiceName, Collection = AgentSignatureDocument.CollectionName, }; var commandResource = new MongoDbAtlasResource { - Db = arguments.ServiceName, - Collection = CommandDocument.CollectionName + Db = arguments.ServiceName, Collection = DeltaDocument.CollectionName, }; var leaseResource = new MongoDbAtlasResource { - Db = arguments.ServiceName, - Collection = LeaseDocument.CollectionName + Db = arguments.ServiceName, Collection = LeaseDocument.CollectionName, }; var tagResources = new MongoDbAtlasResource { - Db = arguments.ServiceName, - Collection = TagDocument.CollectionName + Db = arguments.ServiceName, Collection = TagDocument.CollectionName, }; var roleActions = new[] { - new MongoDbAtlasRoleAction - { - Action = "LIST_DATABASES", - Resources = new[] - { - allClusterResources - } - }, - new MongoDbAtlasRoleAction - { - Action = "LIST_COLLECTIONS", - Resources = new[] - { - allDbResources - } - }, + new MongoDbAtlasRoleAction { Action = "LIST_DATABASES", Resources = new[] { allClusterResources } }, + new MongoDbAtlasRoleAction { Action = "LIST_COLLECTIONS", Resources = new[] { allDbResources } }, new MongoDbAtlasRoleAction { Action = "FIND", - Resources = new[] - { - agentSignatureResource, - commandResource, - leaseResource, - tagResources - } + Resources = new[] { agentSignatureResource, commandResource, leaseResource, tagResources }, }, new MongoDbAtlasRoleAction { Action = "INSERT", - Resources = new[] - { - agentSignatureResource, - commandResource, - leaseResource, - tagResources - } + Resources = new[] { agentSignatureResource, commandResource, leaseResource, tagResources }, }, new MongoDbAtlasRoleAction { Action = "CREATE_INDEX", - Resources = new[] - { - agentSignatureResource, - commandResource, - leaseResource, - tagResources - } + Resources = new[] { agentSignatureResource, commandResource, leaseResource, tagResources }, }, - new MongoDbAtlasRoleAction - { - Action = "REMOVE", - Resources = new[] - { - leaseResource, - tagResources - } - } + new MongoDbAtlasRoleAction { Action = "REMOVE", Resources = new[] { leaseResource, tagResources } }, }; await mongoDbAtlasClient.CreateRole @@ -134,14 +76,19 @@ await mongoDbAtlasClient.CreateRole public static void AddTo(Command parentCommand) { - var createRole = new Command("create-role") - { - Handler = CommandHandler.Create(Execute) - }; + var createRole = new Command("create-role") { Handler = CommandHandler.Create(Execute) }; AddMongoDbAtlasArguments(createRole); AddServiceNameArgument(createRole); parentCommand.AddCommand(createRole); } + + public record Arguments + ( + string GroupName, + string PublicKey, + string PrivateKey, + string ServiceName + ); } diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs index f4fdf916..bb517894 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs @@ -6,15 +6,6 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; internal class CreateUser : CommandBase { - public record Arguments - ( - string GroupName, - string PublicKey, - string PrivateKey, - string ServiceName, - string ServicePassword - ); - private static async Task Execute(Arguments arguments) { const string adminDatabaseName = "admin"; @@ -39,11 +30,7 @@ private static async Task Execute(Arguments arguments) var roles = new[] { - new MongoDbAtlasUserRole - { - DatabaseName = adminDatabaseName, - RoleName = arguments.ServiceName - } + new MongoDbAtlasUserRole { DatabaseName = adminDatabaseName, RoleName = arguments.ServiceName }, }; await mongoDbAtlasClient.CreateUser @@ -67,4 +54,13 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createUser); } + + public record Arguments + ( + string GroupName, + string PublicKey, + string PrivateKey, + string ServiceName, + string ServicePassword + ); } diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs index 83df1ff9..c22776a3 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs @@ -7,16 +7,6 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Serverless; internal class CreateCollectionsServerless : CommandBase { - public record Arguments - ( - string GroupName, - string PublicKey, - string PrivateKey, - string ServiceName, - string ServicePassword, - string InstanceName - ); - private static async Task Execute(Arguments arguments) { const string expectedProtocol = "mongodb+srv://"; @@ -39,14 +29,14 @@ private static async Task Execute(Arguments arguments) new MongoClient( $"{expectedProtocol}{arguments.ServiceName}:{arguments.ServicePassword}@{serverlessInstance.ConnectionStrings.StandardSrv[expectedProtocol.Length..]}/admin"); - await mongoClient.ProvisionTransactionCollections(arguments.ServiceName); + await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } protected static void AddServerlessNameArgument(Command command) { var clusterNameArgument = new Argument("instance-name") { - Description = "The name of the Serverless Instance on which the database will be provisioned." + Description = "The name of the Serverless Instance on which the database will be provisioned.", }; command.AddArgument(clusterNameArgument); @@ -56,7 +46,7 @@ public static void AddTo(Command parentCommand) { var createCollections = new Command("create-collections") { - Handler = CommandHandler.Create(Execute) + Handler = CommandHandler.Create(Execute), }; AddMongoDbAtlasArguments(createCollections); @@ -66,4 +56,14 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createCollections); } + + public record Arguments + ( + string GroupName, + string PublicKey, + string PrivateKey, + string ServiceName, + string ServicePassword, + string InstanceName + ); } diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbServerlessCommand.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs similarity index 100% rename from src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbServerlessCommand.cs rename to src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/CommandBase.cs b/src/EntityDb.Provisioner/Commands/MongoDb/CommandBase.cs index 59a1abc9..b3ba4e86 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/CommandBase.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/CommandBase.cs @@ -12,17 +12,17 @@ protected static void AddMongoDbAtlasArguments(Command command) { var groupName = new Argument("group-name") { - Description = "The name of the MongoDb Atlas Group/Project to access via API." + Description = "The name of the MongoDb Atlas Group/Project to access via API.", }; var publicKey = new Argument("public-key") { - Description = "The public key used to authenticate via API." + Description = "The public key used to authenticate via API.", }; var privateKey = new Argument("private-key") { - Description = "The private key used to authenticate via API." + Description = "The private key used to authenticate via API.", }; command.AddArgument(groupName); @@ -34,7 +34,7 @@ protected static void AddServiceNameArgument(Command command) { var serviceName = new Argument("service-name") { - Description = "The name of the service that will use this database." + Description = "The name of the service that will use this database.", }; serviceName.AddValidator(serviceNameResult => @@ -43,7 +43,8 @@ protected static void AddServiceNameArgument(Command command) if (!ServiceNameRegex.IsMatch(serviceName)) { - serviceNameResult.ErrorMessage = "The service name must begin with an letter, and can only contain letters."; + serviceNameResult.ErrorMessage = + "The service name must begin with an letter, and can only contain letters."; } }); @@ -54,7 +55,7 @@ protected static void AddServicePasswordArgument(Command command) { var servicePassword = new Argument("service-password") { - Description = "The password for the service that will use this database." + Description = "The password for the service that will use this database.", }; command.AddArgument(servicePassword); diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs index 46500f08..c5055f39 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs @@ -7,24 +7,18 @@ namespace EntityDb.Provisioner.Commands.MongoDb; internal class CreateCollections : CommandBase { - public record Arguments - ( - string ServiceName, - string ConnectionString - ); - private static async Task Execute(Arguments arguments) { var mongoClient = new MongoClient(arguments.ConnectionString); - await mongoClient.ProvisionTransactionCollections(arguments.ServiceName); + await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } private static void AddConnectionStringArgument(Command command) { var connectionStringArgument = new Argument("connection-string") { - Description = "The connection string to the mongodb instance." + Description = "The connection string to the mongodb instance.", }; command.AddArgument(connectionStringArgument); @@ -34,7 +28,7 @@ public static void AddTo(Command parentCommand) { var createCollectionsDirect = new Command("create-collections") { - Handler = CommandHandler.Create(Execute) + Handler = CommandHandler.Create(Execute), }; AddServiceNameArgument(createCollectionsDirect); @@ -42,4 +36,10 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createCollectionsDirect); } + + public record Arguments + ( + string ServiceName, + string ConnectionString + ); } diff --git a/src/EntityDb.Provisioner/Commands/Npgsql/CommandBase.cs b/src/EntityDb.Provisioner/Commands/Npgsql/CommandBase.cs deleted file mode 100644 index e2df8ec4..00000000 --- a/src/EntityDb.Provisioner/Commands/Npgsql/CommandBase.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.CommandLine; -using System.Text.RegularExpressions; - -namespace EntityDb.Provisioner.Commands.Npgsql; - -internal abstract class CommandBase -{ - private static readonly Regex ServiceNameRegex = new("^[a-z][a-z]*$", RegexOptions.IgnoreCase); - - protected static void AddConnectionStringArgument(Command command) - { - var connectionString = new Argument("connection-string") - { - Description = "The connection string for the database." - }; - - command.AddArgument(connectionString); - } - - protected static void AddServiceNameArgument(Command command) - { - var serviceName = new Argument("service-name") - { - Description = "The name of the service that will use this database." - }; - - serviceName.AddValidator(serviceNameResult => - { - var serviceName = serviceNameResult.GetValueOrDefault() ?? string.Empty; - - if (!ServiceNameRegex.IsMatch(serviceName)) - { - serviceNameResult.ErrorMessage = "The service name must begin with an letter, and can only contain letters."; - } - }); - - command.AddArgument(serviceName); - } - - protected static void AddServicePasswordArgument(Command command) - { - var servicePassword = new Argument("service-password") - { - Description = "The password for the service that will use this database." - }; - - command.AddArgument(servicePassword); - } -} diff --git a/src/EntityDb.Provisioner/Commands/Npgsql/CreateDatabase.cs b/src/EntityDb.Provisioner/Commands/Npgsql/CreateDatabase.cs deleted file mode 100644 index 412ef242..00000000 --- a/src/EntityDb.Provisioner/Commands/Npgsql/CreateDatabase.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Npgsql; -using System.CommandLine; -using System.CommandLine.NamingConventionBinder; - -namespace EntityDb.Provisioner.Commands.Npgsql; - -internal class CreateDatabase : CommandBase -{ - public record Arguments - ( - string ConnectionString, - string ServiceName - ); - - private static async Task Execute(Arguments arguments) - { - var npgsqlConnection = new NpgsqlConnection(arguments.ConnectionString); - - await npgsqlConnection.OpenAsync(); - - await new global::Npgsql.NpgsqlCommand($"CREATE DATABASE {arguments.ServiceName}", npgsqlConnection).ExecuteNonQueryAsync(); - - await npgsqlConnection.CloseAsync(); - } - - public static void AddTo(Command parentCommand) - { - var createCollectionsDirect = new Command("create-database") - { - Handler = CommandHandler.Create(Execute) - }; - - AddConnectionStringArgument(createCollectionsDirect); - AddServiceNameArgument(createCollectionsDirect); - - parentCommand.AddCommand(createCollectionsDirect); - } -} diff --git a/src/EntityDb.Provisioner/Commands/Npgsql/CreateRole.cs b/src/EntityDb.Provisioner/Commands/Npgsql/CreateRole.cs deleted file mode 100644 index 98f85fef..00000000 --- a/src/EntityDb.Provisioner/Commands/Npgsql/CreateRole.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Npgsql; -using System.CommandLine; -using System.CommandLine.NamingConventionBinder; - -namespace EntityDb.Provisioner.Commands.Npgsql; - -internal class CreateRole : CommandBase -{ - public record Arguments - ( - string ConnectionString, - string ServiceName, - string ServicePassword - ); - - private static async Task Execute(Arguments arguments) - { - var npgsqlConnection = new NpgsqlConnection(arguments.ConnectionString); - - await npgsqlConnection.OpenAsync(); - - await npgsqlConnection.ChangeDatabaseAsync(arguments.ServiceName.ToLowerInvariant()); - - var commands = new[] - { - $"DROP ROLE IF EXISTS {arguments.ServiceName}", - $"CREATE USER {arguments.ServiceName} PASSWORD '{arguments.ServicePassword}'", - $"GRANT SELECT, INSERT ON TABLE agentsignatures, commands, leases, tags TO {arguments.ServiceName}", - $"GRANT DELETE ON TABLE leases, tags TO {arguments.ServiceName}" - }; - - foreach (var command in commands) - { - await new global::Npgsql.NpgsqlCommand(command, npgsqlConnection).ExecuteNonQueryAsync(); - } - - await npgsqlConnection.CloseAsync(); - } - - public static void AddTo(Command parentCommand) - { - var createRole = new Command("create-role") - { - Handler = CommandHandler.Create(Execute) - }; - - AddConnectionStringArgument(createRole); - AddServiceNameArgument(createRole); - AddServicePasswordArgument(createRole); - - parentCommand.AddCommand(createRole); - } -} diff --git a/src/EntityDb.Provisioner/Commands/Npgsql/CreateTables.cs b/src/EntityDb.Provisioner/Commands/Npgsql/CreateTables.cs deleted file mode 100644 index 1f793353..00000000 --- a/src/EntityDb.Provisioner/Commands/Npgsql/CreateTables.cs +++ /dev/null @@ -1,42 +0,0 @@ -using EntityDb.Npgsql.Extensions; -using Npgsql; -using System.CommandLine; -using System.CommandLine.NamingConventionBinder; - -namespace EntityDb.Provisioner.Commands.Npgsql; - -internal class CreateTables : CommandBase -{ - public record Arguments - ( - string ConnectionString, - string ServiceName, - string ServicePassword - ); - - private static async Task Execute(Arguments arguments) - { - var npgsqlConnection = new NpgsqlConnection(arguments.ConnectionString); - - await npgsqlConnection.OpenAsync(); - - await npgsqlConnection.ChangeDatabaseAsync(arguments.ServiceName.ToLowerInvariant()); - - await npgsqlConnection.ProvisionTables(); - - await npgsqlConnection.CloseAsync(); - } - - public static void AddTo(Command parentCommand) - { - var createRole = new Command("create-tables") - { - Handler = CommandHandler.Create(Execute) - }; - - AddConnectionStringArgument(createRole); - AddServiceNameArgument(createRole); - - parentCommand.AddCommand(createRole); - } -} diff --git a/src/EntityDb.Provisioner/Commands/Npgsql/NpgsqlCommand.cs b/src/EntityDb.Provisioner/Commands/Npgsql/NpgsqlCommand.cs deleted file mode 100644 index 47bd1b7f..00000000 --- a/src/EntityDb.Provisioner/Commands/Npgsql/NpgsqlCommand.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.CommandLine; - -namespace EntityDb.Provisioner.Commands.Npgsql; - -internal class NpgsqlCommand -{ - public static void AddTo(Command parentCommand) - { - var npgsql = new Command("npgsql"); - - CreateDatabase.AddTo(npgsql); - CreateTables.AddTo(npgsql); - CreateRole.AddTo(npgsql); - - parentCommand.AddCommand(npgsql); - } -} diff --git a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj index caefc339..c061428c 100644 --- a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj +++ b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj @@ -1,26 +1,25 @@  - - - Exe - + + + Exe + - - - true - entitydb-provisioner - EntityDb EventSourcing DDD CQRS - A dotnet tool for provisioning databases. - + + + true + entitydb-provisioner + EntityDb EventSourcing DDD CQRS + A dotnet tool for provisioning databases. + - - - - + + + + - - - - + + + diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/Resource.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs similarity index 100% rename from src/EntityDb.Provisioner/MongoDbAtlas/Models/Resource.cs rename to src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/RoleAction.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs similarity index 100% rename from src/EntityDb.Provisioner/MongoDbAtlas/Models/RoleAction.cs rename to src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/UserRole.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs similarity index 100% rename from src/EntityDb.Provisioner/MongoDbAtlas/Models/UserRole.cs rename to src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs index 20cd7196..4208230b 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs @@ -4,5 +4,6 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; internal class ServerlessInstance { - [JsonPropertyName("connectionStrings")] public ServerlessConnectionStrings? ConnectionStrings { get; set; } + [JsonPropertyName("connectionStrings")] + public ServerlessConnectionStrings? ConnectionStrings { get; set; } } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs b/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs index 0379dac3..8756fcc3 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs @@ -91,14 +91,14 @@ public async Task UserExists(string databaseName, string username) var getUserResponse = await Send(() => new HttpRequestMessage { Method = HttpMethod.Get, - RequestUri = GetUri($"groups/{_groupId}/databaseUsers/{databaseName}/{username}") + RequestUri = GetUri($"groups/{_groupId}/databaseUsers/{databaseName}/{username}"), }); return getUserResponse.StatusCode switch { HttpStatusCode.OK => true, HttpStatusCode.NotFound => false, - _ => throw new InvalidOperationException() + _ => throw new InvalidOperationException(), }; } @@ -106,15 +106,14 @@ public async Task RoleExists(string role) { var getRoleResponse = await Send(() => new HttpRequestMessage { - Method = HttpMethod.Get, - RequestUri = GetUri($"groups/{_groupId}/customDBRoles/roles/{role}") + Method = HttpMethod.Get, RequestUri = GetUri($"groups/{_groupId}/customDBRoles/roles/{role}"), }); return getRoleResponse.StatusCode switch { HttpStatusCode.OK => true, HttpStatusCode.NotFound => false, - _ => throw new InvalidOperationException() + _ => throw new InvalidOperationException(), }; } @@ -128,7 +127,7 @@ public async Task CreateRole(string roleName, MongoDbAtlasRoleAction[] act { Method = HttpMethod.Post, RequestUri = GetUri($"groups/{_groupId}/customDBRoles/roles"), - Content = content + Content = content, }); if (createRoleResponse.IsSuccessStatusCode) @@ -148,9 +147,7 @@ public async Task CreateUser(string databaseName, string username, string var createUserResponse = await Send(() => new HttpRequestMessage { - Method = HttpMethod.Post, - RequestUri = GetUri($"groups/{_groupId}/databaseUsers"), - Content = content + Method = HttpMethod.Post, RequestUri = GetUri($"groups/{_groupId}/databaseUsers"), Content = content, }); if (createUserResponse.IsSuccessStatusCode) @@ -165,8 +162,7 @@ public async Task CreateUser(string databaseName, string username, string { var getServerlessInstanceResponse = await Send(() => new HttpRequestMessage { - Method = HttpMethod.Get, - RequestUri = GetUri($"groups/{_groupId}/serverless/{instanceName}") + Method = HttpMethod.Get, RequestUri = GetUri($"groups/{_groupId}/serverless/{instanceName}"), }); if (!getServerlessInstanceResponse.IsSuccessStatusCode) @@ -183,8 +179,7 @@ public async Task CreateUser(string databaseName, string username, string { var getClusterResponse = await Send(() => new HttpRequestMessage { - Method = HttpMethod.Get, - RequestUri = GetUri($"groups/{_groupId}/clusters/{clusterName}") + Method = HttpMethod.Get, RequestUri = GetUri($"groups/{_groupId}/clusters/{clusterName}"), }); if (!getClusterResponse.IsSuccessStatusCode) diff --git a/src/EntityDb.Provisioner/Program.cs b/src/EntityDb.Provisioner/Program.cs index 532869c4..971549ad 100644 --- a/src/EntityDb.Provisioner/Program.cs +++ b/src/EntityDb.Provisioner/Program.cs @@ -1,5 +1,4 @@ using EntityDb.Provisioner.Commands.MongoDb; -using EntityDb.Provisioner.Commands.Npgsql; using System.CommandLine; #if DEBUG @@ -16,6 +15,5 @@ var rootCommand = new RootCommand(); MongoDbCommand.AddTo(rootCommand); -NpgsqlCommand.AddTo(rootCommand); return await rootCommand.InvokeAsync(args); diff --git a/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerPool.cs b/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs similarity index 100% rename from src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerPool.cs rename to src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs diff --git a/src/EntityDb.Redis/EntityDb.Redis.csproj b/src/EntityDb.Redis/EntityDb.Redis.csproj index 01d9f39e..67a7da8d 100644 --- a/src/EntityDb.Redis/EntityDb.Redis.csproj +++ b/src/EntityDb.Redis/EntityDb.Redis.csproj @@ -7,12 +7,12 @@ - + - - + + \ No newline at end of file diff --git a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs index 4c43a6ec..c9fd9945 100644 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs +++ b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs @@ -2,7 +2,7 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; -using EntityDb.Redis.Sessions; +using EntityDb.Redis.Snapshots.Sessions; namespace EntityDb.Redis.Snapshots; diff --git a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs index c77070e9..cee3f3d1 100644 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs +++ b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs @@ -3,7 +3,7 @@ using EntityDb.Common.Envelopes; using EntityDb.Common.Snapshots; using EntityDb.Redis.ConnectionMultiplexers; -using EntityDb.Redis.Sessions; +using EntityDb.Redis.Snapshots.Sessions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -52,19 +52,9 @@ private async Task CreateSession(RedisSnapshotSessionOptions opti CancellationToken cancellationToken) { var connectionMultiplexer = - await _connectionMultiplexerFactory.CreateConnectionMultiplexer(options.ConnectionString, cancellationToken); + await _connectionMultiplexerFactory.CreateConnectionMultiplexer(options.ConnectionString, + cancellationToken); return RedisSession.Create(_serviceProvider, connectionMultiplexer.GetDatabase(), options); } - - public static RedisSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, - string connectionString, string keyNamespace) - { - return ActivatorUtilities.CreateInstance> - ( - serviceProvider, - connectionString, - keyNamespace - ); - } } diff --git a/src/EntityDb.Redis/Sessions/IRedisSession.cs b/src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs similarity index 85% rename from src/EntityDb.Redis/Sessions/IRedisSession.cs rename to src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs index 9f936efd..cf739261 100644 --- a/src/EntityDb.Redis/Sessions/IRedisSession.cs +++ b/src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs @@ -2,7 +2,7 @@ using EntityDb.Abstractions.ValueObjects; using StackExchange.Redis; -namespace EntityDb.Redis.Sessions; +namespace EntityDb.Redis.Snapshots.Sessions; internal interface IRedisSession : IDisposableResource { diff --git a/src/EntityDb.Redis/Sessions/RedisSession.cs b/src/EntityDb.Redis/Snapshots/Sessions/RedisSession.cs similarity index 92% rename from src/EntityDb.Redis/Sessions/RedisSession.cs rename to src/EntityDb.Redis/Snapshots/Sessions/RedisSession.cs index 6876adf5..7a92f7ac 100644 --- a/src/EntityDb.Redis/Sessions/RedisSession.cs +++ b/src/EntityDb.Redis/Snapshots/Sessions/RedisSession.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using StackExchange.Redis; -namespace EntityDb.Redis.Sessions; +namespace EntityDb.Redis.Snapshots.Sessions; internal sealed record RedisSession ( @@ -39,7 +39,7 @@ public async Task Insert(Pointer snapshotPointer, RedisValue redisValue) Logger .LogInformation ( - "Finished Running Redis Insert on `{DatabaseIndex}.{RedisKey}`\n\nInserted: {Inserted}", + "Finished Running Redis Insert on `{DatabaseIndex}.{RedisKey}`\n\nCommitted: {Committed}", Database.Database, redisKey.ToString(), inserted @@ -132,7 +132,7 @@ private void AssertNotReadOnly() private RedisKey GetSnapshotKey(Pointer snapshotPointer) { - return $"{Options.KeyNamespace}#{snapshotPointer.Id.Value}@{snapshotPointer.VersionNumber.Value}"; + return $"{Options.KeyNamespace}#{snapshotPointer}"; } public static IRedisSession Create diff --git a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs similarity index 78% rename from src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs rename to src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs index 223cd5c0..fca686ae 100644 --- a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs @@ -2,21 +2,22 @@ using StackExchange.Redis; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.Redis.Sessions; +namespace EntityDb.Redis.Snapshots.Sessions; /// -/// Configuration options for the Redis implementation of . +/// Configuration options for the Redis implementation of . /// public sealed class RedisSnapshotSessionOptions { /// - /// A connection string that is compatible with + /// A connection string that is compatible with /// public string ConnectionString { get; set; } = default!; /// /// Choose a key namespace for snapshots. Snapshots are stored with keys in the following format: - /// {KeyNamespace}#{SnapshotId}@{SnapshotVersionNumber} + /// {KeyNamespace}#{SnapshotId}@{SnapshotVersion} or + /// {KeyNamespace}#{SnapshotId}/{SnapshotBranchName}@{SnapshotVersion} /// public string KeyNamespace { get; set; } = default!; @@ -30,7 +31,7 @@ public sealed class RedisSnapshotSessionOptions /// public bool SecondaryPreferred { get; set; } - /// + /// [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs index af33dc2d..a2ae6728 100644 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs +++ b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs @@ -18,6 +18,10 @@ internal sealed record AgentSignatureDocument : DocumentBase, IEntitiesDocument< private static readonly AgentSignatureSortBuilder SortBuilder = new(); public Id[] EntityIds { get; init; } = Array.Empty(); + public Pointer[] EntityPointers { get; init; } = Array.Empty(); + + public Id[] GetSubjectIds() => EntityIds; + public Pointer[] GetSubjectPointers() => EntityPointers; public static IDocumentReader DocumentReader { get; } = new AgentSignatureDocumentReader(); @@ -42,12 +46,16 @@ ITransaction transaction { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityIds = transaction.Commands - .Select(transactionCommand => transactionCommand.EntityId) + EntityIds = transaction.Subjects + .Select(transactionCommand => transactionCommand.SubjectId) + .Distinct() + .ToArray(), + EntityPointers = transaction.Subjects + .Select(transactionCommand => transactionCommand.SubjectId + transactionCommand.SubjectVersionNumber) .Distinct() .ToArray(), DataType = transaction.AgentSignature.GetType().Name, - Data = envelopeService.Serialize(transaction.AgentSignature) + Data = envelopeService.Serialize(transaction.AgentSignature), } } ); diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs index b3c0269a..2d543720 100644 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs @@ -32,6 +32,9 @@ internal sealed record CommandDocument : DocumentBase, IEntityDocument EntityId; + public VersionNumber GetSubjectVersionNumber() => EntityVersionNumber; + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, @@ -48,8 +51,8 @@ ITransactionCommand transactionCommand { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, + EntityId = transactionCommand.SubjectId, + EntityVersionNumber = transactionCommand.SubjectVersionNumber, DataType = transactionCommand.Data.GetType().Name, Data = envelopeService.Serialize(transactionCommand.Data) } diff --git a/src/EntityDb.SqlDb/Documents/DocumentBase.cs b/src/EntityDb.SqlDb/Documents/DocumentBase.cs index 81202498..f547ec9d 100644 --- a/src/EntityDb.SqlDb/Documents/DocumentBase.cs +++ b/src/EntityDb.SqlDb/Documents/DocumentBase.cs @@ -9,4 +9,7 @@ internal abstract record DocumentBase public Id TransactionId { get; init; } public string DataType { get; init; } = default!; public string Data { get; init; } = default!; + + public Id GetSourceId() => TransactionId; + public TimeStamp GetSourceTimeStamp() => TransactionTimeStamp; } diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs index 3fb8bb32..b16d0250 100644 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs @@ -24,7 +24,10 @@ internal sealed record LeaseDocument : DocumentBase, IEntityDocument EntityId; + public VersionNumber GetSubjectVersionNumber() => EntityVersionNumber; + public static IDocumentReader DocumentReader { get; } = new LeaseDocumentReader(); public static IDocumentReader TransactionIdDocumentReader { get; } = new LeaseTransactionIdDocumentReader(); @@ -48,8 +51,8 @@ ITransactionCommand transactionCommand { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, + EntityId = transactionCommand.SubjectId, + EntityVersionNumber = transactionCommand.SubjectVersionNumber, Scope = lease.Scope, Label = lease.Label, Value = lease.Value, @@ -81,7 +84,7 @@ ITransactionCommand transactionCommand ) { var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.EntityId, transactionCommand.DeleteLeases); + new DeleteLeasesQuery(transactionCommand.SubjectId, transactionCommand.DeleteLeases); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs index 046cbe9f..10e480a3 100644 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs @@ -23,6 +23,9 @@ internal sealed record TagDocument : DocumentBase, IEntityDocument public string Value { get; init; } = default!; public Id EntityId { get; init; } public VersionNumber EntityVersionNumber { get; init; } + + public Id GetSubjectId() => EntityId; + public VersionNumber GetSubjectVersionNumber() => EntityVersionNumber; public static IDocumentReader DocumentReader { get; } = new TagDocumentReader(); @@ -47,8 +50,8 @@ ITransactionCommand transactionCommand { TransactionTimeStamp = transaction.TimeStamp, TransactionId = transaction.Id, - EntityId = transactionCommand.EntityId, - EntityVersionNumber = transactionCommand.EntityVersionNumber, + EntityId = transactionCommand.SubjectId, + EntityVersionNumber = transactionCommand.SubjectVersionNumber, Label = tag.Label, Value = tag.Value, DataType = tag.GetType().Name, @@ -78,7 +81,7 @@ public static DeleteDocumentsCommand GetDeleteCommand ITransactionCommand transactionCommand ) { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.EntityId, transactionCommand.DeleteTags); + var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.SubjectId, transactionCommand.DeleteTags); return new DeleteDocumentsCommand ( diff --git a/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs b/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs index 86b9968b..d9b1b218 100644 --- a/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs +++ b/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs @@ -47,7 +47,7 @@ CancellationToken cancellationToken ( sqlDbSession, TDocument.TransactionIdDocumentReader, - documents => documents.Select(document => document.TransactionId), + documents => documents.Select(document => document.GetSourceId()), cancellationToken ); } @@ -65,7 +65,7 @@ CancellationToken cancellationToken ( sqlDbSession, TDocument.EntityIdDocumentReader, - documents => documents.Select(document => document.EntityId), + documents => documents.Select(document => document.GetSubjectId()), cancellationToken ); } @@ -83,7 +83,7 @@ CancellationToken cancellationToken ( sqlDbSession, TDocument.EntityIdsDocumentReader, - documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.EntityIds)), + documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.GetSubjectIds())), cancellationToken ); } @@ -111,7 +111,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken } } - public static IAsyncEnumerable> EnumerateEntityAnnotation + public static IAsyncEnumerable> EnumerateEntityAnnotation ( this DocumentQuery documentQuery, ISqlDbSession sqlDbSession, @@ -132,7 +132,7 @@ CancellationToken cancellationToken return documents.EnumerateEntityAnnotation(envelopeService, cancellationToken); } - public static IAsyncEnumerable> EnumerateEntitiesAnnotation + public static IAsyncEnumerable> EnumerateEntitiesAnnotation ( this DocumentQuery documentQuery, ISqlDbSession sqlDbSession, diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs index a26111fe..17c0f8b7 100644 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs +++ b/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs @@ -11,6 +11,11 @@ public IFilterDefinition SubjectIdsIn(params Id[] subjectIds) { return AnyIn(nameof(AgentSignatureDocument.EntityIds), subjectIds); } + + public IFilterDefinition SubjectPointersIn(params Pointer[] subjectPointers) + { + return AnyIn(nameof(AgentSignatureDocument.EntityPointers), subjectPointers); + } public IFilterDefinition AgentSignatureTypeIn(params Type[] agentSignatureTypes) { diff --git a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs index fd1819a2..7966c6bd 100644 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs +++ b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs @@ -129,7 +129,7 @@ public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, .EnumerateData(_sqlDbSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument @@ -137,7 +137,7 @@ public IAsyncEnumerable> EnumerateAnnotatedAgentSign .EnumerateEntitiesAnnotation(_sqlDbSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, + public IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, CancellationToken cancellationToken = default) { return CommandDocument @@ -153,17 +153,17 @@ public async Task PutTransaction(ITransaction transaction, CancellationTok await PutAgentSignature(transaction, cancellationToken); - foreach (var transactionCommand in transaction.Commands) + foreach (var transactionCommand in transaction.Subjects) { cancellationToken.ThrowIfCancellationRequested(); - VersionZeroReservedException.ThrowIfZero(transactionCommand.EntityVersionNumber); + VersionZeroReservedException.ThrowIfZero(transactionCommand.SubjectVersionNumber); var previousVersionNumber = await CommandDocument - .GetLastEntityVersionNumber(_sqlDbSession, transactionCommand.EntityId, cancellationToken); + .GetLastEntityVersionNumber(_sqlDbSession, transactionCommand.SubjectId, cancellationToken); OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber.Next(), - transactionCommand.EntityVersionNumber); + transactionCommand.SubjectVersionNumber); await PutCommand(transaction, transactionCommand, cancellationToken); } diff --git a/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs index 13ce083c..346a3041 100644 --- a/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; using EntityDb.Void.Transactions; using Microsoft.Extensions.DependencyInjection; @@ -10,7 +10,7 @@ namespace EntityDb.Void.Extensions; public static class ServiceCollectionExtensions { /// - /// Adds a production-ready implementation of to a service + /// Adds a production-ready implementation of to a service /// collection. /// /// The service collection. @@ -20,6 +20,6 @@ public static class ServiceCollectionExtensions public static void AddVoidTransactions(this IServiceCollection serviceCollection) { serviceCollection - .AddSingleton(); + .AddSingleton(); } } diff --git a/src/EntityDb.Void/Transactions/VoidTransactionRepository.cs b/src/EntityDb.Void/Transactions/VoidSourceRepository.cs similarity index 54% rename from src/EntityDb.Void/Transactions/VoidTransactionRepository.cs rename to src/EntityDb.Void/Transactions/VoidSourceRepository.cs index d1f2d774..eea3cc67 100644 --- a/src/EntityDb.Void/Transactions/VoidTransactionRepository.cs +++ b/src/EntityDb.Void/Transactions/VoidSourceRepository.cs @@ -1,61 +1,62 @@ using EntityDb.Abstractions.Annotations; using EntityDb.Abstractions.Leases; using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Subjects; using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; namespace EntityDb.Void.Transactions; -internal sealed class VoidTransactionRepository : DisposableResourceBaseClass, ITransactionRepository +internal sealed class VoidSourceRepository : DisposableResourceBaseClass, ISourceRepository { - public IAsyncEnumerable EnumerateTransactionIds(IAgentSignatureQuery agentSignatureQuery, + public IAsyncEnumerable EnumerateSourceIds(IAgentSignatureQuery agentSignatureQuery, CancellationToken cancellationToken = default) { return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateTransactionIds(ICommandQuery commandQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceSubjectQuery sourceSubjectQuery, CancellationToken cancellationToken = default) { return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateTransactionIds(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default) { return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateTransactionIds(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, CancellationToken cancellationToken = default) { return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateEntityIds(IAgentSignatureQuery agentSignatureQuery, + public IAsyncEnumerable EnumerateSubjectPointers(IAgentSignatureQuery agentSignatureQuery, CancellationToken cancellationToken = default) { - return AsyncEnumerable.Empty(); + return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateEntityIds(ICommandQuery commandQuery, + public IAsyncEnumerable EnumerateSubjectPointers(ISourceSubjectQuery sourceSubjectQuery, CancellationToken cancellationToken = default) { - return AsyncEnumerable.Empty(); + return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateEntityIds(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateSubjectPointers(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default) { - return AsyncEnumerable.Empty(); + return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateEntityIds(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateSubjectPointers(ITagQuery tagQuery, CancellationToken cancellationToken = default) { - return AsyncEnumerable.Empty(); + return AsyncEnumerable.Empty(); } public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, @@ -64,7 +65,7 @@ public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery ag return AsyncEnumerable.Empty(); } - public IAsyncEnumerable EnumerateCommands(ICommandQuery commandQuery, + public IAsyncEnumerable EnumerateSubjects(ISourceSubjectQuery sourceSubjectQuery, CancellationToken cancellationToken = default) { return AsyncEnumerable.Empty(); @@ -82,19 +83,20 @@ public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, return AsyncEnumerable.Empty(); } - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + IAgentSignatureQuery agentSignatureQuery, CancellationToken cancellationToken = default) { - return AsyncEnumerable.Empty>(); + return AsyncEnumerable.Empty>(); } - public IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, + public IAsyncEnumerable> EnumerateAnnotatedSubjects(ISourceSubjectQuery sourceSubjectQuery, CancellationToken cancellationToken = default) { - return AsyncEnumerable.Empty>(); + return AsyncEnumerable.Empty>(); } - public Task PutTransaction(ITransaction transaction, + public Task Put(ISource source, CancellationToken cancellationToken = default) { return Task.FromResult(false); diff --git a/src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs b/src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs new file mode 100644 index 00000000..daa00cc8 --- /dev/null +++ b/src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs @@ -0,0 +1,16 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Disposables; + +namespace EntityDb.Void.Transactions; + +internal class VoidSourceRepositoryFactory : DisposableResourceBaseClass, ISourceRepositoryFactory +{ + private static readonly Task VoidTransactionRepositoryTask = + Task.FromResult(new VoidSourceRepository() as ISourceRepository); + + public Task CreateRepository( + string sourceSessionOptionsName, CancellationToken cancellationToken = default) + { + return VoidTransactionRepositoryTask.WaitAsync(cancellationToken); + } +} diff --git a/src/EntityDb.Void/Transactions/VoidTransactionRepositoryFactory.cs b/src/EntityDb.Void/Transactions/VoidTransactionRepositoryFactory.cs deleted file mode 100644 index 75a4f9ce..00000000 --- a/src/EntityDb.Void/Transactions/VoidTransactionRepositoryFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Disposables; - -namespace EntityDb.Void.Transactions; - -internal class VoidTransactionRepositoryFactory : DisposableResourceBaseClass, ITransactionRepositoryFactory -{ - private static readonly Task VoidTransactionRepositoryTask = - Task.FromResult(new VoidTransactionRepository() as ITransactionRepository); - - public Task CreateRepository( - string transactionSessionOptionsName, CancellationToken cancellationToken = default) - { - return VoidTransactionRepositoryTask.WaitAsync(cancellationToken); - } -} diff --git a/test/Directory.Build.props b/test/Directory.Build.props index ab7359f9..abdd609b 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -14,13 +14,13 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - + diff --git a/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs b/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs index 8cf0215b..4481a801 100644 --- a/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs +++ b/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Agents; +using EntityDb.Abstractions.Sources.Agents; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -40,7 +40,7 @@ public void GivenBackingServiceInactive_WhenGettingAgent_ThenThrow() // ASSERT - Should.Throw(() => agentAccessor.GetAgentAsync(default!)); + Should.Throw(() => agentAccessor.GetAgent(default!)); } [Fact] @@ -60,7 +60,7 @@ public void GivenBackingServiceActive_WhenGettingAgent_ThenReturnAgent() // ACT - var agent = agentAccessor.GetAgentAsync(default!); + var agent = agentAccessor.GetAgent(default!); // ASSERT @@ -84,7 +84,7 @@ public async Task GivenBackingServiceActive_ThenCanGetTimestamp() var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ASSERT @@ -106,7 +106,7 @@ public async Task GivenBackingServiceActive_WhenGettingAgentSignature_ThenReturn var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ACT @@ -135,7 +135,7 @@ public async Task var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ACT @@ -160,13 +160,13 @@ public async Task var expectedApplicationInfo = new Dictionary { - ["UserId"] = Guid.NewGuid().ToString() + ["UserId"] = Guid.NewGuid().ToString(), }; var agentSignatureAugmenterMock = new Mock(MockBehavior.Strict); agentSignatureAugmenterMock - .Setup(x => x.GetApplicationInfoAsync(It.IsAny())) + .Setup(x => x.GetApplicationInfo(It.IsAny())) .ReturnsAsync(expectedApplicationInfo); using var serviceScope = CreateServiceScope(serviceCollection => @@ -178,7 +178,7 @@ public async Task var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ACT diff --git a/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs b/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs index 7da7c61c..a45ca800 100644 --- a/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs +++ b/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs @@ -1,4 +1,4 @@ -using EntityDb.Common.Agents; +using EntityDb.Common.Sources.Agents; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.Common.Tests.Agents; @@ -25,7 +25,7 @@ protected override IEnumerable GetAgentAccessorOptions() { return new[] { - new object() + new object(), }; } diff --git a/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs b/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs index 7b760a2e..d1316702 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs @@ -5,4 +5,4 @@ namespace EntityDb.Common.Tests; [CollectionDefinition(nameof(DatabaseContainerCollection))] public class DatabaseContainerCollection : ICollectionFixture { -} +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs index f0cefb39..6d282c9d 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs @@ -1,7 +1,6 @@ using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; -using Testcontainers.PostgreSql; using Testcontainers.Redis; using Xunit; @@ -23,22 +22,9 @@ public class DatabaseContainerFixture : IAsyncLifetime .WithImage("redis:7.2.0") .Build(); - - private sealed class WaitUntil : IWaitUntil - { - private static readonly string[] LineEndings = new string[2] { "\r\n", "\n" }; - - public async Task UntilAsync(IContainer container) - { - var (text, text2) = await container.GetLogs(default, default, timestampsEnabled: false).ConfigureAwait(continueOnCapturedContext: false); - return 2.Equals(Array.Empty().Concat(text.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)).Concat(text2.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) - .Count((string line) => line.Contains("Waiting for connections"))); - } - } - public IContainer MongoDbContainer { get; } = new ContainerBuilder() .WithImage("mongo:7.0.0") - .WithPortBinding(27017, assignRandomHostPort: true) + .WithPortBinding(27017, true) .WithBindMount(DockerVolumeMongoDbInit, "/docker-entrypoint-initdb.d") .WithEnvironment("MONGO_INITDB_ROOT_USERNAME", null) .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", null) @@ -46,24 +32,28 @@ public async Task UntilAsync(IContainer container) .WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil())) .Build(); - public PostgreSqlContainer PostgreSqlContainer { get; } = new PostgreSqlBuilder() - .WithImage("postgres:15.4") - .WithDatabase(OmniParameter) - .WithUsername(OmniParameter) - .WithPassword(OmniParameter) - .Build(); - public async Task InitializeAsync() { await RedisContainer.StartAsync(); await MongoDbContainer.StartAsync(); - await PostgreSqlContainer.StartAsync(); } public async Task DisposeAsync() { await RedisContainer.DisposeAsync(); await MongoDbContainer.DisposeAsync(); - await PostgreSqlContainer.DisposeAsync(); } -} + + private sealed class WaitUntil : IWaitUntil + { + private static readonly string[] LineEndings = { "\r\n", "\n" }; + + public async Task UntilAsync(IContainer container) + { + var (text, text2) = await container.GetLogs(timestampsEnabled: false).ConfigureAwait(false); + return 2.Equals(Array.Empty().Concat(text.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) + .Concat(text2.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) + .Count(line => line.Contains("Waiting for connections"))); + } + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index e4125c6b..7bc5e4f5 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -1,58 +1,60 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Queries; +using System.Diagnostics.CodeAnalysis; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Builders; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; using EntityDb.Common.Polyfills; -using EntityDb.Common.Tests.Implementations.Commands; +using EntityDb.Common.Tests.Implementations.Deltas; using EntityDb.Common.Tests.Implementations.Snapshots; using Microsoft.Extensions.DependencyInjection; using Moq; using Shouldly; using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Entities; [Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] public class EntityTests : TestsBase { - public EntityTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base(serviceProvider, databaseContainerFixture) + public EntityTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base( + serviceProvider, databaseContainerFixture) { } - private static async Task BuildTransaction + private static async Task BuildSource ( IServiceScope serviceScope, Id entityId, - VersionNumber from, - VersionNumber to, + Version from, + Version to, TEntity? entity = default ) where TEntity : class, IEntity, ISnapshotWithTestLogic { - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, entityId); - if (entity is not null) transactionBuilder.Load(entity); + if (entity is not null) sourceBuilder.Load(entity); for (var i = from; i.Value <= to.Value; i = i.Next()) { - if (transactionBuilder.IsEntityKnown() && - transactionBuilder.GetEntity().VersionNumber.Value >= i.Value) continue; + if (sourceBuilder.IsEntityKnown() && + sourceBuilder.GetEntity().Pointer.Version.Value >= i.Value) continue; - transactionBuilder.Append(new DoNothing()); + sourceBuilder.Append(new DoNothing()); } - return transactionBuilder.Build(Id.NewId()); + return sourceBuilder.Build(Id.NewId()); } private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -60,13 +62,13 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe const ulong n = 10UL; const ulong m = 5UL; - var versionNumberN = new VersionNumber(n); + var versionN = new Version(n); - var versionNumberM = new VersionNumber(m); + var versionM = new Version(m); using var serviceScope = CreateServiceScope(serviceCollection => { - transactionsAdder.AddDependencies.Invoke(serviceCollection); + sourcesAdder.AddDependencies.Invoke(serviceCollection); entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); }); @@ -77,84 +79,82 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe .CreateRepository(TestSessionOptions.Write, TestSessionOptions.Write); - var transaction = await BuildTransaction(serviceScope, entityId, new VersionNumber(1), versionNumberN); + var source = await BuildSource(serviceScope, entityId, new Version(1), versionN); - var transactionInserted = await entityRepository.PutTransaction(transaction); + var sourceCommitted = await entityRepository.Commit(source); // ARRANGE ASSERTIONS - transactionInserted.ShouldBeTrue(); + sourceCommitted.ShouldBeTrue(); // ACT - var entityAtVersionM = await entityRepository.GetSnapshot(entityId + versionNumberM); + var entityAtVersionM = await entityRepository.GetSnapshot(entityId + versionM); // ASSERT - entityAtVersionM.VersionNumber.ShouldBe(versionNumberM); + entityAtVersionM.Pointer.Version.ShouldBe(versionM); } - private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetCommandsRuns( + private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetDeltasRuns( EntityAdder entityAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE - var expectedVersionNumber = new VersionNumber(10); + var expectedVersion = new Version(10); var entityId = Id.NewId(); - var commands = new List(); + var deltas = new List(); - var transactionRepositoryMock = new Mock(MockBehavior.Strict); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); - transactionRepositoryMock - .Setup(repository => repository.PutTransaction(It.IsAny(), It.IsAny())) - .ReturnsAsync((ITransaction transaction, CancellationToken _) => + sourceRepositoryMock + .Setup(repository => + repository.Commit(It.IsAny(), It.IsAny())) + .ReturnsAsync((Source source, CancellationToken _) => { - foreach (var transactionCommand in transaction.Commands) - { - commands.Add(transactionCommand.Data); - } + deltas.AddRange(source.Messages.Select(message => message.Delta)); return true; }); - transactionRepositoryMock + sourceRepositoryMock .Setup(factory => factory.DisposeAsync()) .Returns(ValueTask.CompletedTask); - transactionRepositoryMock - .Setup(repository => repository.EnumerateCommands(It.IsAny(), It.IsAny())) - .Returns(() => AsyncEnumerablePolyfill.FromResult(commands)) + sourceRepositoryMock + .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Returns(() => AsyncEnumerablePolyfill.FromResult(deltas)) .Verifiable(); - var transactionRepositoryFactoryMock = - new Mock(MockBehavior.Strict); + var sourceRepositoryFactoryMock = + new Mock(MockBehavior.Strict); - transactionRepositoryFactoryMock + sourceRepositoryFactoryMock .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) - .ReturnsAsync(transactionRepositoryMock.Object); + .ReturnsAsync(sourceRepositoryMock.Object); using var serviceScope = CreateServiceScope(serviceCollection => { entityAdder.AddDependencies.Invoke(serviceCollection); - serviceCollection.AddSingleton(transactionRepositoryFactoryMock.Object); + serviceCollection.AddSingleton(sourceRepositoryFactoryMock.Object); }); await using var entityRepository = await serviceScope.ServiceProvider .GetRequiredService>() .CreateRepository(TestSessionOptions.Write); - var transaction = - await BuildTransaction(serviceScope, entityId, new VersionNumber(1), expectedVersionNumber); + var source = + await BuildSource(serviceScope, entityId, new Version(1), expectedVersion); - var transactionInserted = await entityRepository.PutTransaction(transaction); + var sourceCommitted = await entityRepository.Commit(source); // ARRANGE ASSERTIONS - transactionInserted.ShouldBeTrue(); + sourceCommitted.ShouldBeTrue(); // ACT @@ -162,10 +162,11 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T // ASSERT - currenEntity.VersionNumber.ShouldBe(expectedVersionNumber); + currenEntity.Pointer.Version.ShouldBe(expectedVersion); - transactionRepositoryMock - .Verify(repository => repository.EnumerateCommands(It.IsAny(), It.IsAny()), + sourceRepositoryMock + .Verify( + repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), Times.Once); } @@ -180,7 +181,7 @@ private async Task { entityAdder.AddDependencies.Invoke(serviceCollection); - serviceCollection.AddSingleton(GetMockedTransactionRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); }); // ACT @@ -210,7 +211,7 @@ private async Task { entityAdder.AddDependencies.Invoke(serviceCollection); - serviceCollection.AddSingleton(GetMockedTransactionRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory()); }); @@ -241,7 +242,7 @@ private async Task { entityAdder.AddDependencies.Invoke(serviceCollection); - serviceCollection.AddSingleton(GetMockedTransactionRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory()); }); @@ -262,25 +263,25 @@ private async Task } private async Task - Generic_GivenSnapshotAndNewCommands_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( + Generic_GivenSnapshotAndNewDeltas_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( EntityAdder entityAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE - var snapshot = TEntity.Construct(default).WithVersionNumber(new VersionNumber(1)); + var snapshot = TEntity.Construct(default).WithVersion(new Version(1)); - var newCommands = new object[] + var newDeltas = new object[] { new DoNothing(), - new DoNothing() + new DoNothing(), }; using var serviceScope = CreateServiceScope(serviceCollection => { entityAdder.AddDependencies.Invoke(serviceCollection); - serviceCollection.AddSingleton(GetMockedTransactionRepositoryFactory(newCommands)); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(newDeltas)); serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory(snapshot)); }); @@ -296,8 +297,8 @@ private async Task snapshotOrDefault.ShouldNotBe(default); snapshotOrDefault.ShouldNotBe(snapshot); - snapshotOrDefault.VersionNumber.ShouldBe( - new VersionNumber(snapshot.VersionNumber.Value + Convert.ToUInt64(newCommands.Length))); + snapshotOrDefault.Pointer.Version.ShouldBe( + new Version(snapshot.Pointer.Version.Value + Convert.ToUInt64(newDeltas.Length))); } private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow( @@ -310,7 +311,7 @@ private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_The { entityAdder.AddDependencies.Invoke(serviceCollection); - serviceCollection.AddSingleton(GetMockedTransactionRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); }); await using var entityRepository = await serviceScope.ServiceProvider @@ -326,20 +327,20 @@ await Should.ThrowAsync(async () => } [Theory] - [MemberData(nameof(AddTransactionsAndEntitySnapshots))] - public Task GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM(TransactionsAdder transactionsAdder, + [MemberData(nameof(AddSourcesAndEntitySnapshots))] + public Task GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM(SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) { return RunGenericTestAsync ( new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder } + new object?[] { sourcesAdder, entitySnapshotAdder } ); } [Theory] [MemberData(nameof(AddEntity))] - public Task GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetCommandsRuns(EntityAdder entityAdder) + public Task GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetDeltasRuns(EntityAdder entityAdder) { return RunGenericTestAsync ( @@ -387,7 +388,7 @@ public Task [Theory] [MemberData(nameof(AddEntity))] - public Task GivenSnapshotAndNewCommands_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( + public Task GivenSnapshotAndNewDeltas_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( EntityAdder entityAdder) { return RunGenericTestAsync diff --git a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj index d83a4dd7..f1872e80 100644 --- a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj +++ b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj @@ -1,24 +1,20 @@  - - - - - + + + - - - - - - + + + + - - - Always - - + + + Always + + diff --git a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensions.cs b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs similarity index 91% rename from test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensions.cs rename to test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs index 45f315bf..610a3046 100644 --- a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensions.cs +++ b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Common.Extensions; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Common.Sources.Queries.FilterBuilders; using Moq; using Shouldly; using Xunit; @@ -55,7 +55,7 @@ public void AndTruthTable() var truthTable = new DoubleInput[] { - new(false, false, false), new(false, true, false), new(true, false, false), new(true, true, true) + new(false, false, false), new(false, true, false), new(true, false, false), new(true, true, true), }; var filterBuilder = GetFilterBuilder(); @@ -80,7 +80,7 @@ public void OrTruthTable() var truthTable = new DoubleInput[] { - new(false, false, false), new(false, true, true), new(true, false, true), new(true, true, true) + new(false, false, false), new(false, true, true), new(true, false, true), new(true, true, true), }; var filterBuilder = GetFilterBuilder(); @@ -104,7 +104,7 @@ public void NandTruthTable() var truthTable = new DoubleInput[] { - new(false, false, true), new(false, true, true), new(true, false, true), new(true, true, false) + new(false, false, true), new(false, true, true), new(true, false, true), new(true, true, false), }; var filterBuilder = GetFilterBuilder(); @@ -128,7 +128,7 @@ public void NorTruthTable() var truthTable = new DoubleInput[] { - new(false, false, true), new(false, true, false), new(true, false, false), new(true, true, false) + new(false, false, true), new(false, true, false), new(true, false, false), new(true, true, false), }; var filterBuilder = GetFilterBuilder(); @@ -153,7 +153,7 @@ public void XorTruthTable() var truthTable = new DoubleInput[] { - new(false, false, false), new(false, true, true), new(true, false, true), new(true, true, false) + new(false, false, false), new(false, true, true), new(true, false, true), new(true, true, false), }; var filterBuilder = GetFilterBuilder(); @@ -177,7 +177,7 @@ public void XnorTruthTable() var truthTable = new DoubleInput[] { - new(false, false, true), new(false, true, false), new(true, false, false), new(true, true, true) + new(false, false, true), new(false, true, false), new(true, false, false), new(true, true, true), }; var filterBuilder = GetFilterBuilder(); diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs deleted file mode 100644 index 670614b3..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs +++ /dev/null @@ -1,54 +0,0 @@ -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.States; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Tests.Implementations.Entities; -using EntityDb.Common.Tests.Implementations.Projections; - -namespace EntityDb.Common.Tests.Implementations.Commands; - -public record DoNothing : IReducer, IMutator -{ - public void Mutate(OneToOneProjection projection) - { - projection.VersionNumber = projection.VersionNumber.Next(); - } - - public TestEntity Reduce(TestEntity entity) - { - return entity with { VersionNumber = entity.VersionNumber.Next() }; - } -} - -public record AddLease(ILease Lease) : DoNothing, IAddLeasesCommand -{ - public IEnumerable GetLeases(TestEntity testEntity) - { - yield return Lease; - } -} - -public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesCommand -{ - public IEnumerable GetLeases(TestEntity testEntity) - { - yield return Lease; - } -} - -public record AddTag(ITag Tag) : DoNothing, IAddTagsCommand -{ - public IEnumerable GetTags(TestEntity testEntity) - { - yield return Tag; - } -} - -public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsCommand -{ - public IEnumerable GetTags(TestEntity testEntity) - { - yield return Tag; - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs deleted file mode 100644 index 70c73d7c..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs +++ /dev/null @@ -1,37 +0,0 @@ -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.States; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Tests.Implementations.Entities; -using EntityDb.Common.Tests.Implementations.Leases; -using EntityDb.Common.Tests.Implementations.Projections; -using EntityDb.Common.Tests.Implementations.Tags; - -namespace EntityDb.Common.Tests.Implementations.Commands; - -public record StoreNumber(ulong Number) : IReducer, IMutator, IAddLeasesCommand, IAddTagsCommand -{ - public void Mutate(OneToOneProjection projection) - { - projection.VersionNumber = projection.VersionNumber.Next(); - } - - public TestEntity Reduce(TestEntity entity) - { - return entity with - { - VersionNumber = entity.VersionNumber.Next() - }; - } - - public IEnumerable GetLeases(TestEntity testEntity) - { - yield return new CountLease(Number); - } - - public IEnumerable GetTags(TestEntity testEntity) - { - yield return new CountTag(Number); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs b/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs deleted file mode 100644 index 9db73896..00000000 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ /dev/null @@ -1,60 +0,0 @@ -using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.EntityFramework.DbContexts; -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace EntityDb.Common.Tests.Implementations.DbContexts; - -internal class GenericDbContext : EntityDbContextBase, IEntityDbContext> - where TSnapshot : class, ISnapshotWithTestLogic -{ - private readonly ILoggerFactory _loggerFactory; - private readonly EntityFrameworkSnapshotSessionOptions _options; - - public GenericDbContext(ILoggerFactory loggerFactory, EntityFrameworkSnapshotSessionOptions options) - { - _loggerFactory = loggerFactory; - _options = options; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder - .UseNpgsql($"{_options.ConnectionString};Include Error Detail=true") - .UseLoggerFactory(_loggerFactory) - .EnableSensitiveDataLogging(); - } - - public static GenericDbContext Construct(IServiceProvider serviceProvider, EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, entityFrameworkSnapshotSessionOptions); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.ApplyConfiguration(new SnapshotReferenceTypeConfiguration()); - modelBuilder.ApplyConfiguration(new SnapshotTypeConfiguration()); - } -} - -public class SnapshotReferenceTypeConfiguration : EntityFramework.Snapshots.SnapshotReferenceTypeConfiguration - where TSnapshot : class, ISnapshotWithTestLogic -{ - public SnapshotReferenceTypeConfiguration() : base($"{typeof(TSnapshot).Name}SnapshotReferences") - { - } -} - -public class SnapshotTypeConfiguration : IEntityTypeConfiguration - where TSnapshot : class, ISnapshotWithTestLogic -{ - public virtual void Configure(EntityTypeBuilder snapshotBuilder) - { - TSnapshot.Configure(snapshotBuilder); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs new file mode 100644 index 00000000..657657b2 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs @@ -0,0 +1,13 @@ +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Entities; + +namespace EntityDb.Common.Tests.Implementations.Deltas; + +public record AddLease(ILease Lease) : DoNothing, IAddLeasesDelta +{ + public IEnumerable GetLeases(TestEntity entity) + { + yield return Lease; + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs new file mode 100644 index 00000000..96788805 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs @@ -0,0 +1,13 @@ +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Entities; + +namespace EntityDb.Common.Tests.Implementations.Deltas; + +public record AddTag(ITag Tag) : DoNothing, IAddTagsDelta +{ + public IEnumerable GetTags(TestEntity entity) + { + yield return Tag; + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs new file mode 100644 index 00000000..2a4b6d92 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs @@ -0,0 +1,13 @@ +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Entities; + +namespace EntityDb.Common.Tests.Implementations.Deltas; + +public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesDelta +{ + public IEnumerable GetLeases(TestEntity entity) + { + yield return Lease; + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs new file mode 100644 index 00000000..329b3d93 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs @@ -0,0 +1,13 @@ +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Entities; + +namespace EntityDb.Common.Tests.Implementations.Deltas; + +public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsDelta +{ + public IEnumerable GetTags(TestEntity entity) + { + yield return Tag; + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs new file mode 100644 index 00000000..3d822364 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions.Snapshots.Transforms; +using EntityDb.Common.Tests.Implementations.Entities; +using EntityDb.Common.Tests.Implementations.Projections; + +namespace EntityDb.Common.Tests.Implementations.Deltas; + +public record DoNothing : IReducer, IMutator +{ + public void Mutate(OneToOneProjection projection) + { + projection.Pointer = projection.Pointer.Next(); + } + + public TestEntity Reduce(TestEntity entity) + { + return new TestEntity { Pointer = entity.Pointer.Next() }; + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs new file mode 100644 index 00000000..ab6b24c8 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs @@ -0,0 +1,33 @@ +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Snapshots.Transforms; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Entities; +using EntityDb.Common.Tests.Implementations.Leases; +using EntityDb.Common.Tests.Implementations.Projections; +using EntityDb.Common.Tests.Implementations.Tags; + +namespace EntityDb.Common.Tests.Implementations.Deltas; + +public record StoreNumber(ulong Number) : IReducer, IMutator, + IAddLeasesDelta, IAddTagsDelta +{ + public IEnumerable GetLeases(TestEntity entity) + { + yield return new CountLease(Number); + } + + public IEnumerable GetTags(TestEntity entity) + { + yield return new CountTag(Number); + } + + public void Mutate(OneToOneProjection projection) + { + projection.Pointer = projection.Pointer.Next(); + } + + public TestEntity Reduce(TestEntity entity) + { + return new TestEntity { Pointer = entity.Pointer.Next() }; + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index bce362fb..c1142f4f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,56 +1,34 @@ -using System.Linq.Expressions; -using EntityDb.Abstractions.States; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Snapshots.Transforms; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Snapshots; -using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Entities; public record TestEntity : IEntity, ISnapshotWithTestLogic { - public required Id Id { get; init; } - public VersionNumber VersionNumber { get; init; } - - public static TestEntity Construct(Id entityId) + public static TestEntity Construct(Pointer pointer) { return new TestEntity { - Id = entityId, + Pointer = pointer, }; } - public static void Configure(EntityTypeBuilder testEntityBuilder) - { - testEntityBuilder - .HasKey(testEntity => new - { - testEntity.Id, - testEntity.VersionNumber, - }); - } - - public Id GetId() - { - return Id; - } - - public VersionNumber GetVersionNumber() + public Pointer GetPointer() { - return VersionNumber; + return Pointer; } - public static bool CanReduce(object command) + public static bool CanReduce(object delta) { - return command is IReducer; + return delta is IReducer; } - public TestEntity Reduce(object command) + public TestEntity Reduce(object delta) { - if (command is IReducer reducer) - { - return reducer.Reduce(this); - } + if (delta is IReducer reducer) return reducer.Reduce(this); throw new NotSupportedException(); } @@ -62,23 +40,21 @@ public bool ShouldRecord() public bool ShouldRecordAsLatest(TestEntity? previousMostRecentSnapshot) { - return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousMostRecentSnapshot); + return ShouldRecordAsLatestLogic.Value is not null && + ShouldRecordAsLatestLogic.Value.Invoke(this, previousMostRecentSnapshot); } + public required Pointer Pointer { get; init; } + public static string MongoDbCollectionName => "TestEntities"; public static string RedisKeyNamespace => "test-entity"; - public TestEntity WithVersionNumber(VersionNumber versionNumber) + public TestEntity WithVersion(Version version) { - return this with { VersionNumber = versionNumber }; + return new TestEntity { Pointer = Pointer.Id + version }; } public static AsyncLocal?> ShouldRecordLogic { get; } = new(); public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); - - public Expression> GetKeyPredicate() - { - return (testEntity) => testEntity.Id == Id && testEntity.VersionNumber == VersionNumber; - } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs b/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs index 442d23cd..35717cfb 100644 --- a/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Leases; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Leases; diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index cfb01a61..b7ce4702 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,71 +1,44 @@ -using System.Linq.Expressions; using System.Runtime.CompilerServices; -using EntityDb.Abstractions.States; +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Snapshots.Transforms; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Extensions; -using EntityDb.Common.Projections; -using EntityDb.Common.Queries; +using EntityDb.Common.Sources.Queries.Standard; using EntityDb.Common.Tests.Implementations.Snapshots; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; +using Microsoft.Extensions.DependencyInjection; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Projections; public class OneToOneProjection : IProjection, ISnapshotWithTestLogic { - public required Id Id { get; set; } - public VersionNumber VersionNumber { get; set; } - public TimeStamp LastTransactionAt { get; set; } + public TimeStamp LastSourceAt { get; set; } - public static OneToOneProjection Construct(Id projectionId) + public static OneToOneProjection Construct(Pointer pointer) { return new OneToOneProjection - { - Id = projectionId, + { + Pointer = pointer, }; } - public static void Configure(EntityTypeBuilder oneToOneProjectionBuilder) - { - oneToOneProjectionBuilder - .HasKey(oneToOneProjection => new - { - oneToOneProjection.Id, - oneToOneProjection.VersionNumber, - }); - } - - public Id GetId() + public Pointer GetPointer() { - return Id; + return Pointer; } - public VersionNumber GetVersionNumber() + public void Mutate(Source source) { - return VersionNumber; - } + LastSourceAt = source.TimeStamp; - public void Mutate(ISource source) - { - if (source is not ITransaction transaction) + foreach (var message in source.Messages) { - throw new NotSupportedException(); - } - - LastTransactionAt = transaction.TimeStamp; - - foreach (var command in transaction.Commands) - { - if (command.Data is not IMutator mutator) - { - continue; - } + if (message.Delta is not IMutator mutator) continue; mutator.Mutate(this); - VersionNumber = command.EntityVersionNumber; + Pointer = message.EntityPointer; } } @@ -76,55 +49,48 @@ public bool ShouldRecord() public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) { - return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); + return ShouldRecordAsLatestLogic.Value is not null && + ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); } - public async IAsyncEnumerable EnumerateSources + public async IAsyncEnumerable EnumerateSources ( - ISourceRepository sourceRepository, + IServiceProvider serviceProvider, Pointer projectionPointer, [EnumeratorCancellation] CancellationToken cancellationToken ) { - var query = new GetEntityCommandsQuery(projectionPointer, VersionNumber); + var query = new GetDeltasQuery(projectionPointer, default); + + var sourceRepository = await serviceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.ReadOnly, cancellationToken); - var transactionIds = await sourceRepository.TransactionRepository - .EnumerateTransactionIds(query, cancellationToken) + var sourceIds = await sourceRepository + .EnumerateSourceIds(query, cancellationToken) .ToArrayAsync(cancellationToken); - foreach (var transactionId in transactionIds) - { - yield return await sourceRepository.TransactionRepository - .GetTransaction(transactionId, cancellationToken); - } + foreach (var sourceId in sourceIds) + yield return await sourceRepository + .GetSource(sourceId, cancellationToken); } - public static IEnumerable EnumerateProjectionIds(ISource source) + public static IEnumerable EnumerateEntityIds(Source source) { - var projectionIds = new HashSet(); - - if (source is ITransaction transaction) - { - foreach (var command in transaction.Commands) - { - if (command.Data is not IMutator) - { - continue; - } - - projectionIds.Add(command.EntityId); - } - } - - return projectionIds; + return source.Messages + .Where(message => message.Delta is IMutator) + .Select(message => message.EntityPointer.Id) + .Distinct(); } + public required Pointer Pointer { get; set; } + public static string MongoDbCollectionName => "OneToOneProjections"; public static string RedisKeyNamespace => "one-to-one-projection"; - public OneToOneProjection WithVersionNumber(VersionNumber versionNumber) + public OneToOneProjection WithVersion(Version version) { - VersionNumber = versionNumber; + Pointer = Pointer.Id + version; return this; } @@ -133,9 +99,4 @@ public OneToOneProjection WithVersionNumber(VersionNumber versionNumber) public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); - - public Expression> GetKeyPredicate() - { - return (oneToOneProjection) => oneToOneProjection.Id == Id && oneToOneProjection.VersionNumber == VersionNumber; - } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs index 09d9713c..a5e7d1a3 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Common.Tests.Implementations.Leases; using EntityDb.Common.Tests.Implementations.Tags; @@ -16,7 +16,7 @@ public TFilter GetFilter(ILeaseFilterBuilder builder) { return builder.And ( - builder.LeaseTypeIn(typeof(CountLease)), + builder.DataTypeIn(typeof(CountLease)), builder.Or ( Enumerable @@ -39,7 +39,7 @@ public TFilter GetFilter(ITagFilterBuilder builder) { return builder.And ( - builder.TagTypeIn(typeof(CountTag)), + builder.DataTypeIn(typeof(CountTag)), builder.Or ( Enumerable diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityBranchQuery.cs similarity index 57% rename from test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Queries/EntityBranchQuery.cs index 1db26fad..bb3d7e2b 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/EntityBranchQuery.cs @@ -1,47 +1,52 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Tests.Implementations.Queries; -public record EntityIdQuery(Id EntityId, object? Options = null) : IAgentSignatureQuery, ICommandQuery, ILeaseQuery, ITagQuery +public record EntityBranchQuery(Id EntityId, object? Options = null) : IMessageGroupQuery, IMessageQuery, + ILeaseQuery, ITagQuery { - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) + public TFilter GetFilter(ILeaseFilterBuilder builder) { - return builder.SubjectIdsIn(EntityId); + return builder.EntityIdIn(EntityId); } - public TSort GetSort(IAgentSignatureSortBuilder builder) + public TSort GetSort(ILeaseSortBuilder builder) { - return builder.EntityIds(true); + return builder.Combine + ( + builder.EntityId(true), + builder.EntityVersion(true) + ); } - public TFilter GetFilter(ICommandFilterBuilder builder) + public TFilter GetFilter(IMessageGroupFilterBuilder builder) { - return builder.EntityIdIn(EntityId); + return builder.AnyEntityIdIn(EntityId); } - public TSort GetSort(ICommandSortBuilder builder) + public TSort GetSort(IMessageGroupSortBuilder builder) { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); + return builder.EntityIds(true); } - public TFilter GetFilter(ILeaseFilterBuilder builder) + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(IMessageFilterBuilder builder) { return builder.EntityIdIn(EntityId); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(IMessageSortBuilder builder) { return builder.Combine ( builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.EntityVersion(true) ); } @@ -55,11 +60,7 @@ public TSort GetSort(ITagSortBuilder builder) return builder.Combine ( builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.EntityVersion(true) ); } - - public int? Skip => null; - - public int? Take => null; } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionNumberQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionNumberQuery.cs deleted file mode 100644 index 43a82eec..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionNumberQuery.cs +++ /dev/null @@ -1,55 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Tests.Implementations.Queries; - -public record EntityVersionNumberQuery(VersionNumber Gte, VersionNumber Lte, object? Options = null) : ICommandQuery, ILeaseQuery, ITagQuery -{ - public TFilter GetFilter(ICommandFilterBuilder builder) - { - return builder.And - ( - builder.EntityVersionNumberGte(Gte), - builder.EntityVersionNumberLte(Lte) - ); - } - - public TSort GetSort(ICommandSortBuilder builder) - { - return builder.EntityVersionNumber(true); - } - - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.And - ( - builder.EntityVersionNumberGte(Gte), - builder.EntityVersionNumberLte(Lte) - ); - } - - public TSort GetSort(ILeaseSortBuilder builder) - { - return builder.EntityVersionNumber(true); - } - - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.And - ( - builder.EntityVersionNumberGte(Gte), - builder.EntityVersionNumberLte(Lte) - ); - } - - public TSort GetSort(ITagSortBuilder builder) - { - return builder.EntityVersionNumber(true); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs new file mode 100644 index 00000000..dc195f3a --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs @@ -0,0 +1,56 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.Implementations.Queries; + +public record EntityVersionQuery(Version Gte, Version Lte, object? Options = null) : IMessageQuery, + ILeaseQuery, ITagQuery +{ + public TFilter GetFilter(ILeaseFilterBuilder builder) + { + return builder.And + ( + builder.EntityVersionGte(Gte), + builder.EntityVersionLte(Lte) + ); + } + + public TSort GetSort(ILeaseSortBuilder builder) + { + return builder.EntityVersion(true); + } + + public TFilter GetFilter(IMessageFilterBuilder builder) + { + return builder.And + ( + builder.EntityVersionGte(Gte), + builder.EntityVersionLte(Lte) + ); + } + + public TSort GetSort(IMessageSortBuilder builder) + { + return builder.EntityVersion(true); + } + + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagFilterBuilder builder) + { + return builder.And + ( + builder.EntityVersionGte(Gte), + builder.EntityVersionLte(Lte) + ); + } + + public TSort GetSort(ITagSortBuilder builder) + { + return builder.EntityVersion(true); + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/SourceIdQuery.cs similarity index 53% rename from test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Queries/SourceIdQuery.cs index 0fc4b28d..666b70b5 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/SourceIdQuery.cs @@ -1,63 +1,63 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Tests.Implementations.Queries; -public record TransactionIdQuery(Id TransactionId, object? Options = null) : IAgentSignatureQuery, ICommandQuery, ILeaseQuery, +public record SourceIdQuery(Id SourceId, object? Options = null) : IMessageGroupQuery, IMessageQuery, ILeaseQuery, ITagQuery { - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) + public TFilter GetFilter(ILeaseFilterBuilder builder) { - return builder.SourceIdIn(TransactionId); + return builder.SourceIdIn(SourceId); } - public TSort GetSort(IAgentSignatureSortBuilder builder) + public TSort GetSort(ILeaseSortBuilder builder) { return builder.Combine ( - builder.SourceId(true) + builder.SourceId(true), + builder.EntityId(true), + builder.EntityVersion(true) ); } - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(ICommandFilterBuilder builder) + public TFilter GetFilter(IMessageGroupFilterBuilder builder) { - return builder.SourceIdIn(TransactionId); + return builder.SourceIdIn(SourceId); } - public TSort GetSort(ICommandSortBuilder builder) + public TSort GetSort(IMessageGroupSortBuilder builder) { return builder.Combine ( - builder.SourceId(true), - builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.SourceId(true) ); } - public TFilter GetFilter(ILeaseFilterBuilder builder) + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(IMessageFilterBuilder builder) { - return builder.SourceIdIn(TransactionId); + return builder.SourceIdIn(SourceId); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(IMessageSortBuilder builder) { return builder.Combine ( builder.SourceId(true), builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.EntityVersion(true) ); } public TFilter GetFilter(ITagFilterBuilder builder) { - return builder.SourceIdIn(TransactionId); + return builder.SourceIdIn(SourceId); } public TSort GetSort(ITagSortBuilder builder) @@ -66,7 +66,7 @@ public TSort GetSort(ITagSortBuilder builder) ( builder.SourceId(true), builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.EntityVersion(true) ); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/SourceTimeStampQuery.cs similarity index 69% rename from test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Queries/SourceTimeStampQuery.cs index 2cf2db6e..69146713 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/SourceTimeStampQuery.cs @@ -1,14 +1,15 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Tests.Implementations.Queries; -public record TransactionTimeStampQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : IAgentSignatureQuery, ICommandQuery, +public record SourceTimeStampQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : IMessageGroupQuery, + IMessageQuery, ILeaseQuery, ITagQuery { - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) + public TFilter GetFilter(ILeaseFilterBuilder builder) { return builder.And ( @@ -17,19 +18,17 @@ public TFilter GetFilter(IAgentSignatureFilterBuilder builder) ); } - public TSort GetSort(IAgentSignatureSortBuilder builder) + public TSort GetSort(ILeaseSortBuilder builder) { return builder.Combine ( - builder.SourceTimeStamp(true) + builder.SourceTimeStamp(true), + builder.EntityId(true), + builder.EntityVersion(true) ); } - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(ICommandFilterBuilder builder) + public TFilter GetFilter(IMessageGroupFilterBuilder builder) { return builder.And ( @@ -38,17 +37,19 @@ public TFilter GetFilter(ICommandFilterBuilder builder) ); } - public TSort GetSort(ICommandSortBuilder builder) + public TSort GetSort(IMessageGroupSortBuilder builder) { return builder.Combine ( - builder.SourceTimeStamp(true), - builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.SourceTimeStamp(true) ); } - public TFilter GetFilter(ILeaseFilterBuilder builder) + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(IMessageFilterBuilder builder) { return builder.And ( @@ -57,13 +58,13 @@ public TFilter GetFilter(ILeaseFilterBuilder builder) ); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(IMessageSortBuilder builder) { return builder.Combine ( builder.SourceTimeStamp(true), builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.EntityVersion(true) ); } @@ -82,7 +83,7 @@ public TSort GetSort(ITagSortBuilder builder) ( builder.SourceTimeStamp(true), builder.EntityId(true), - builder.EntityVersionNumber(true) + builder.EntityVersion(true) ); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/CommandSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs similarity index 61% rename from test/EntityDb.Common.Tests/Implementations/Seeders/CommandSeeder.cs rename to test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs index 2594b065..69eb78dd 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/CommandSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs @@ -1,8 +1,8 @@ -using EntityDb.Common.Tests.Implementations.Commands; +using EntityDb.Common.Tests.Implementations.Deltas; namespace EntityDb.Common.Tests.Implementations.Seeders; -public static class CommandSeeder +public static class DeltaSeeder { public static object Create() { diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs index 0c32b7de..6abb1195 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Common.Leases; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Seeders; diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs new file mode 100644 index 00000000..cfda5d7e --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs @@ -0,0 +1,28 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Tests.Implementations.Snapshots; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.Implementations.Seeders; + +public static class MessageSeeder +{ + public static IEnumerable CreateFromDeltas(Id entityId, uint numDeltas, + ulong previousVersionValue = 0) + where TEntity : class, IEntity, ISnapshotWithTestLogic + { + var previousVersion = new Version(previousVersionValue); + + for (var versionOffset = 0; versionOffset < numDeltas; versionOffset++) + { + previousVersion = previousVersion.Next(); + + yield return new Message + { + EntityPointer = entityId + previousVersion, + Delta = DeltaSeeder.Create(), + }; + } + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs new file mode 100644 index 00000000..880923c2 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs @@ -0,0 +1,32 @@ +using System.Collections.Immutable; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Agents; +using EntityDb.Common.Tests.Implementations.Snapshots; + +namespace EntityDb.Common.Tests.Implementations.Seeders; + +public static class SourceSeeder +{ + public static Source Create(params Message[] messages) + { + return new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = messages.ToImmutableArray(), + }; + } + + public static Source Create(Id entityId, uint numDeltas, + ulong previousVersionValue = 0) + where TEntity : class, IEntity, ISnapshotWithTestLogic + { + var sourceMessages = MessageSeeder + .CreateFromDeltas(entityId, numDeltas, previousVersionValue).ToArray(); + + return Create(sourceMessages); + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs index 2fc37efa..03ebec8e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Tags; -using EntityDb.Common.Tags; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Seeders; diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs deleted file mode 100644 index 733f807a..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionCommandSeeder.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Immutable; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; -using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.Common.Transactions; - -namespace EntityDb.Common.Tests.Implementations.Seeders; - -public static class TransactionCommandSeeder -{ - public static IEnumerable CreateFromCommands(Id entityId, uint numCommands, ulong previousVersionNumberValue = 0) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - var previousVersionNumber = new VersionNumber(previousVersionNumberValue); - - for (var versionNumberOffset = 0; versionNumberOffset < numCommands; versionNumberOffset++) - { - previousVersionNumber = previousVersionNumber.Next(); - - yield return new TransactionCommand - { - EntityId = entityId, - EntityVersionNumber = previousVersionNumber, - Data = CommandSeeder.Create(), - AddLeases = ImmutableArray.Empty, - DeleteLeases = ImmutableArray.Empty, - AddTags = ImmutableArray.Empty, - DeleteTags = ImmutableArray.Empty, - }; - } - } -} diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs deleted file mode 100644 index 7b81fed7..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Immutable; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Agents; -using EntityDb.Common.Entities; -using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.Common.Transactions; - -namespace EntityDb.Common.Tests.Implementations.Seeders; - -public static class TransactionSeeder -{ - public static ITransaction Create(params ITransactionCommand[] transactionCommands) - { - return new Transaction - { - Id = Id.NewId(), - TimeStamp = TimeStamp.UtcNow, - AgentSignature = new UnknownAgentSignature(new Dictionary()), - Commands = transactionCommands.ToImmutableArray() - }; - } - - public static ITransaction Create(Id entityId, uint numCommands, ulong previousVersionNumberValue = 0) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - var transactionCommands = TransactionCommandSeeder.CreateFromCommands(entityId, numCommands, previousVersionNumberValue).ToArray(); - - return Create(transactionCommands); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs index 81e4ef63..a7917432 100644 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs @@ -1,18 +1,16 @@ +using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Snapshots; -using EntityDb.EntityFramework.Snapshots; -using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Snapshots; -public interface ISnapshotWithTestLogic : ISnapshot, IEntityFrameworkSnapshot +public interface ISnapshotWithTestLogic : ISnapshot where TSnapshot : class { - VersionNumber VersionNumber { get; } + Pointer Pointer { get; } static abstract string MongoDbCollectionName { get; } static abstract string RedisKeyNamespace { get; } static abstract AsyncLocal?> ShouldRecordLogic { get; } static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } - static abstract void Configure(EntityTypeBuilder snapshotBuilder); - TSnapshot WithVersionNumber(VersionNumber versionNumber); + TSnapshot WithVersion(Version version); } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs b/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs index b6030c5a..f40d12ad 100644 --- a/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Tags; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Tags; diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index fbedfa7b..6269573f 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -1,40 +1,42 @@ +using System.Diagnostics.CodeAnalysis; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; -using EntityDb.Common.Projections; using EntityDb.Common.Tests.Implementations.Seeders; using EntityDb.Common.Tests.Implementations.Snapshots; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Projections; [Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] public class ProjectionsTests : TestsBase { - public ProjectionsTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) + public ProjectionsTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) + : base(startupServiceProvider, databaseContainerFixture) { } - private async Task Generic_GivenEmptyTransactionRepository_WhenGettingProjection_ThenThrow( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + private async Task Generic_GivenEmptySourceRepository_WhenGettingProjection_ThenThrow( + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) where TProjection : IProjection { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { - transactionsAdder.AddDependencies.Invoke(serviceCollection); + sourcesAdder.AddDependencies.Invoke(serviceCollection); entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); }); await using var projectionRepository = await serviceScope.ServiceProvider .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write, TestSessionOptions.Write); + .CreateRepository(TestSessionOptions.Write); // ACT & ASSERT @@ -44,27 +46,28 @@ await Should.ThrowAsync(() => private async Task - Generic_GivenTransactionCommitted_WhenGettingProjection_ThenReturnExpectedProjection( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder, + Generic_GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic where TProjection : class, IProjection, ISnapshotWithTestLogic { // ARRANGE - const uint numberOfVersionNumbers = 5; - const uint replaceAtVersionNumber = 3; + const uint numberOfVersions = 5; + const uint replaceAtVersionValue = 3; TProjection.ShouldRecordAsLatestLogic.Value = (projection, _) => - projection.GetVersionNumber() == new VersionNumber(replaceAtVersionNumber); + projection.Pointer.Version == new Version(replaceAtVersionValue); - var projectionId = Id.NewId(); - var firstTransaction = TransactionSeeder.Create(projectionId, replaceAtVersionNumber); - var secondTransaction = TransactionSeeder.Create(projectionId, numberOfVersionNumbers - replaceAtVersionNumber, replaceAtVersionNumber); + var entityId = Id.NewId(); + var firstSource = SourceSeeder.Create(entityId, replaceAtVersionValue); + var secondSource = SourceSeeder.Create(entityId, + numberOfVersions - replaceAtVersionValue, replaceAtVersionValue); using var serviceScope = CreateServiceScope(serviceCollection => { - transactionsAdder.AddDependencies.Invoke(serviceCollection); + sourcesAdder.AddDependencies.Invoke(serviceCollection); entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); }); @@ -75,55 +78,55 @@ private async Task await using var projectionRepository = await serviceScope.ServiceProvider .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write, TestSessionOptions.Write); + .CreateRepository(TestSessionOptions.Write); - var firstTransactionInserted = await entityRepository.PutTransaction(firstTransaction); - var secondTransactionInserted = await entityRepository.PutTransaction(secondTransaction); + var firstSourceCommitted = await entityRepository.Commit(firstSource); + var secondSourceCommitted = await entityRepository.Commit(secondSource); // ARRANGE ASSERTIONS - numberOfVersionNumbers.ShouldBeGreaterThan(replaceAtVersionNumber); + numberOfVersions.ShouldBeGreaterThan(replaceAtVersionValue); - firstTransactionInserted.ShouldBeTrue(); - secondTransactionInserted.ShouldBeTrue(); + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeTrue(); projectionRepository.SnapshotRepository.ShouldNotBeNull(); // ACT - var currentProjection = await projectionRepository.GetSnapshot(projectionId); - var projectionSnapshot = await projectionRepository.SnapshotRepository.GetSnapshotOrDefault(projectionId); + var currentProjection = await projectionRepository.GetSnapshot(entityId); + var projectionSnapshot = await projectionRepository.SnapshotRepository.GetSnapshotOrDefault(entityId); // ASSERT - currentProjection.GetVersionNumber().Value.ShouldBe(numberOfVersionNumbers); + currentProjection.Pointer.Version.Value.ShouldBe(numberOfVersions); - projectionSnapshot.ShouldNotBe(default); + projectionSnapshot.ShouldNotBeNull(); - projectionSnapshot!.GetVersionNumber().Value.ShouldBe(replaceAtVersionNumber); + projectionSnapshot.Pointer.Version.Value.ShouldBe(replaceAtVersionValue); } [Theory] - [MemberData(nameof(AddTransactionsEntitySnapshotsAndProjectionSnapshots))] - public Task GivenEmptyTransactionRepository_WhenGettingProjection_ThenThrow(TransactionsAdder transactionsAdder, + [MemberData(nameof(AddSourcesEntitySnapshotsAndProjectionSnapshots))] + public Task GivenEmptySourceRepository_WhenGettingProjection_ThenThrow(SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) { return RunGenericTestAsync ( new[] { projectionSnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder, projectionSnapshotAdder } + new object?[] { sourcesAdder, entitySnapshotAdder, projectionSnapshotAdder } ); } [Theory] - [MemberData(nameof(AddTransactionsEntitySnapshotsAndProjectionSnapshots))] - public Task GivenTransactionCommitted_WhenGettingProjection_ThenReturnExpectedProjection( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + [MemberData(nameof(AddSourcesEntitySnapshotsAndProjectionSnapshots))] + public Task GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) { return RunGenericTestAsync ( new[] { entitySnapshotAdder.SnapshotType, projectionSnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder, projectionSnapshotAdder } + new object?[] { sourcesAdder, entitySnapshotAdder, projectionSnapshotAdder } ); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs index 3b774941..1fab1edb 100644 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs @@ -1,4 +1,5 @@ -using EntityDb.Abstractions.Snapshots; +using System.Diagnostics.CodeAnalysis; +using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; using EntityDb.Common.Tests.Implementations.Snapshots; @@ -8,18 +9,21 @@ using Moq; using Shouldly; using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Snapshots; [Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] public sealed class SnapshotTests : TestsBase { - public SnapshotTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) + public SnapshotTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : + base(startupServiceProvider, databaseContainerFixture) { } private async Task - Generic_GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInsertedMatchesFetched( + Generic_GivenEmptySnapshotRepository_WhenSnapshotCommittedAndFetched_ThenCommittedMatchesFetched( SnapshotAdder snapshotAdder) where TSnapshot : class, ISnapshotWithTestLogic { @@ -30,8 +34,8 @@ private async Task snapshotAdder.AddDependencies.Invoke(serviceCollection); }); - var snapshotId = Id.NewId(); - var expectedSnapshot = TSnapshot.Construct(snapshotId).WithVersionNumber(new VersionNumber(300)); + var entityId = Id.NewId(); + var expectedSnapshot = TSnapshot.Construct(entityId).WithVersion(new Version(300)); var snapshotRepositoryFactory = serviceScope.ServiceProvider .GetRequiredService>(); @@ -41,13 +45,13 @@ private async Task // ACT - var snapshotInserted = await snapshotRepository.PutSnapshot(snapshotId, expectedSnapshot); + var snapshotCommitted = await snapshotRepository.PutSnapshot(entityId, expectedSnapshot); - var actualSnapshot = await snapshotRepository.GetSnapshotOrDefault(snapshotId); + var actualSnapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); // ASSERT - snapshotInserted.ShouldBeTrue(); + snapshotCommitted.ShouldBeTrue(); actualSnapshot.ShouldBeEquivalentTo(expectedSnapshot); } @@ -55,7 +59,7 @@ private async Task [Theory] [MemberData(nameof(AddEntitySnapshots))] [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInsertedMatchesFetched( + public Task GivenEmptySnapshotRepository_WhenSnapshotCommittedAndFetched_ThenCommittedMatchesFetched( SnapshotAdder snapshotAdder) { return RunGenericTestAsync @@ -66,7 +70,7 @@ public Task GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInse } private async Task - Generic_GivenEmptySnapshotRepository_WhenPuttingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged< + Generic_GivenEmptySnapshotRepository_WhenCommittingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged< TSnapshot>(SnapshotAdder snapshotAdder) where TSnapshot : class, ISnapshotWithTestLogic { @@ -83,7 +87,7 @@ private async Task serviceCollection.AddSingleton(loggerFactory); }); - var snapshot = TSnapshot.Construct(default).WithVersionNumber(new VersionNumber(300)); + var snapshot = TSnapshot.Construct(default).WithVersion(new Version(300)); await using var snapshotRepository = await serviceScope.ServiceProvider .GetRequiredService>() @@ -104,7 +108,7 @@ private async Task [MemberData(nameof(AddEntitySnapshots))] [MemberData(nameof(AddProjectionSnapshots))] public Task - GivenEmptySnapshotRepository_WhenPuttingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged( + GivenEmptySnapshotRepository_WhenCommittingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged( SnapshotAdder snapshotAdder) { return RunGenericTestAsync @@ -115,15 +119,15 @@ public Task } private async Task - Generic_GivenInsertedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot( - SnapshotAdder snapshotAdder) + Generic_GivenCommittedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot( + SnapshotAdder snapshotAdder) where TSnapshot : class, ISnapshotWithTestLogic { // ARRANGE - Pointer latestSnapshotPointer = Id.NewId(); + Pointer latestPointer = Id.NewId(); - var snapshot = TSnapshot.Construct(latestSnapshotPointer.Id).WithVersionNumber(new VersionNumber(5000)); + var snapshot = TSnapshot.Construct(latestPointer).WithVersion(new Version(5000)); using var serviceScope = CreateServiceScope(serviceCollection => { @@ -136,7 +140,7 @@ private async Task .GetRequiredService>() .CreateRepository(TestSessionOptions.Write); - var inserted = await writeSnapshotRepository.PutSnapshot(latestSnapshotPointer, snapshot); + var inserted = await writeSnapshotRepository.PutSnapshot(latestPointer, snapshot); // ARRANGE ASSERTIONS @@ -144,9 +148,9 @@ private async Task // ACT - var deleted = await writeSnapshotRepository.DeleteSnapshots(new[] { latestSnapshotPointer }); + var deleted = await writeSnapshotRepository.DeleteSnapshots(new[] { latestPointer }); - var finalSnapshot = await writeSnapshotRepository.GetSnapshotOrDefault(latestSnapshotPointer); + var finalSnapshot = await writeSnapshotRepository.GetSnapshotOrDefault(latestPointer); // ASSERT @@ -158,7 +162,7 @@ private async Task [Theory] [MemberData(nameof(AddEntitySnapshots))] [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenInsertedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot(SnapshotAdder snapshotAdder) + public Task GivenCommittedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot(SnapshotAdder snapshotAdder) { return RunGenericTestAsync ( @@ -167,15 +171,15 @@ public Task GivenInsertedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapsh ); } - private async Task Generic_GivenInsertedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot( + private async Task Generic_GivenCommittedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot( SnapshotAdder snapshotAdder) where TSnapshot : class, ISnapshotWithTestLogic { // ARRANGE - var snapshotId = Id.NewId(); + var entityId = Id.NewId(); - var expectedSnapshot = TSnapshot.Construct(snapshotId).WithVersionNumber(new VersionNumber(5000)); + var expectedSnapshot = TSnapshot.Construct(entityId).WithVersion(new Version(5000)); using var serviceScope = CreateServiceScope(serviceCollection => { @@ -194,7 +198,7 @@ private async Task Generic_GivenInsertedSnapshot_WhenReadInVariousReadModes_Then .GetRequiredService>() .CreateRepository(TestSessionOptions.ReadOnlySecondaryPreferred); - var inserted = await writeSnapshotRepository.PutSnapshot(snapshotId, expectedSnapshot); + var inserted = await writeSnapshotRepository.PutSnapshot(entityId, expectedSnapshot); // ARRANGE ASSERTIONS @@ -202,10 +206,10 @@ private async Task Generic_GivenInsertedSnapshot_WhenReadInVariousReadModes_Then // ACT - var readOnlySnapshot = await readOnlySnapshotRepository.GetSnapshotOrDefault(snapshotId); + var readOnlySnapshot = await readOnlySnapshotRepository.GetSnapshotOrDefault(entityId); var readOnlySecondaryPreferredSnapshot = - await readOnlySecondaryPreferredSnapshotRepository.GetSnapshotOrDefault(snapshotId); + await readOnlySecondaryPreferredSnapshotRepository.GetSnapshotOrDefault(entityId); // ASSERT @@ -216,7 +220,7 @@ private async Task Generic_GivenInsertedSnapshot_WhenReadInVariousReadModes_Then [Theory] [MemberData(nameof(AddEntitySnapshots))] [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenInsertedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot(SnapshotAdder snapshotAdder) + public Task GivenCommittedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot(SnapshotAdder snapshotAdder) { return RunGenericTestAsync ( diff --git a/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs b/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs index 50e7437d..fab09c51 100644 --- a/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs @@ -1,6 +1,7 @@ -using EntityDb.Abstractions.Snapshots; +using System.Diagnostics.CodeAnalysis; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Snapshots; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -11,6 +12,7 @@ namespace EntityDb.Common.Tests.Snapshots; +[SuppressMessage("ReSharper", "UnusedMember.Local")] public class TryCatchSnapshotRepositoryTests : TestsBase { public TryCatchSnapshotRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) diff --git a/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs similarity index 62% rename from test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs rename to test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs index f4f8b5e4..205484b8 100644 --- a/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs +++ b/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs @@ -1,26 +1,28 @@ +using System.Diagnostics.CodeAnalysis; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Tests.Implementations.Seeders; using EntityDb.Common.Tests.Implementations.Snapshots; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; -namespace EntityDb.Common.Tests.Transactions; +namespace EntityDb.Common.Tests.Sources; [Collection(nameof(DatabaseContainerCollection))] -public class EntitySnapshotTransactionSubscriberTests : TestsBase +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class EntitySnapshotSourceSubscriberTests : TestsBase { - public EntitySnapshotTransactionSubscriberTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) + public EntitySnapshotSourceSubscriberTests(IServiceProvider startupServiceProvider, + DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) { } private async Task - Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotTransactionSubscriber_ThenAlwaysWriteSnapshot< + Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotSourceSubscriber_ThenAlwaysWriteSnapshot< TEntity>( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -29,15 +31,15 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { - transactionsAdder.AddDependencies.Invoke(serviceCollection); + sourcesAdder.AddDependencies.Invoke(serviceCollection); entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); }); var entityId = Id.NewId(); - const uint numberOfVersionNumbers = 10; + const uint numberOfVersions = 10; - var transaction = TransactionSeeder.Create(entityId, numberOfVersionNumbers); + var source = SourceSeeder.Create(entityId, numberOfVersions); await using var entityRepository = await serviceScope.ServiceProvider .GetRequiredService>() @@ -49,32 +51,33 @@ private async Task // ACT - await entityRepository.PutTransaction(transaction); + var committed = await entityRepository.Commit(source); var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); // ASSERT - snapshot.ShouldNotBe(default); - snapshot!.GetVersionNumber().Value.ShouldBe(numberOfVersionNumbers); + committed.ShouldBeTrue(); + snapshot.ShouldNotBeNull(); + snapshot.Pointer.Version.Value.ShouldBe(numberOfVersions); } [Theory] - [MemberData(nameof(AddTransactionsAndEntitySnapshots))] + [MemberData(nameof(AddSourcesAndEntitySnapshots))] private Task - GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotTransactionSubscriber_ThenAlwaysWriteSnapshot( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) + GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotSourceSubscriber_ThenAlwaysWriteSnapshot( + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) { return RunGenericTestAsync ( new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder } + new object?[] { sourcesAdder, entitySnapshotAdder } ); } private async Task - Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotTransactionSubscriber_ThenNeverWriteSnapshot< - TEntity>(TransactionsAdder transactionsAdder, SnapshotAdder snapshotAdder) + Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotSourceSubscriber_ThenNeverWriteSnapshot< + TEntity>(SourcesAdder sourcesAdder, SnapshotAdder snapshotAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -83,13 +86,13 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { - transactionsAdder.AddDependencies.Invoke(serviceCollection); + sourcesAdder.AddDependencies.Invoke(serviceCollection); snapshotAdder.AddDependencies.Invoke(serviceCollection); }); var entityId = Id.NewId(); - var transaction = TransactionSeeder.Create(entityId, 10); + var source = SourceSeeder.Create(entityId, 10); await using var entityRepository = await serviceScope.ServiceProvider .GetRequiredService>() @@ -101,7 +104,7 @@ private async Task // ACT - await entityRepository.PutTransaction(transaction); + await entityRepository.Commit(source); var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); @@ -111,15 +114,15 @@ private async Task } [Theory] - [MemberData(nameof(AddTransactionsAndEntitySnapshots))] + [MemberData(nameof(AddSourcesAndEntitySnapshots))] private Task - GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotTransactionSubscriber_ThenNeverWriteSnapshot( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) + GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotSourceSubscriber_ThenNeverWriteSnapshot( + SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) { return RunGenericTestAsync ( new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder } + new object?[] { sourcesAdder, entitySnapshotAdder } ); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs similarity index 63% rename from test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs rename to test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs index fa70de5c..993ab2c4 100644 --- a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs @@ -1,20 +1,21 @@ -using EntityDb.Abstractions.Commands; +using System.Diagnostics.CodeAnalysis; using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Transactions.Builders; +using EntityDb.Abstractions.Entities.Deltas; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Exceptions; -using EntityDb.Common.Leases; -using EntityDb.Common.Tests.Implementations.Commands; +using EntityDb.Common.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Deltas; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; -namespace EntityDb.Common.Tests.Transactions; +namespace EntityDb.Common.Tests.Sources; -public class SingleEntityTransactionBuilderTests : TestsBase +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class SingleEntitySourceBuilderTests : TestsBase { - public SingleEntityTransactionBuilderTests(IServiceProvider serviceProvider) : base(serviceProvider) + public SingleEntitySourceBuilderTests(IServiceProvider serviceProvider) : base(serviceProvider) { } @@ -28,15 +29,15 @@ private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, default); // ASSERT - transactionBuilder.IsEntityKnown().ShouldBeFalse(); + sourceBuilder.IsEntityKnown().ShouldBeFalse(); - Should.Throw(() => transactionBuilder.GetEntity()); + Should.Throw(() => sourceBuilder.GetEntity()); } private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity( @@ -55,29 +56,29 @@ private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpected var expectedEntity = TEntity .Construct(expectedEntityId); - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, expectedEntityId); - transactionBuilder.Load(expectedEntity); + sourceBuilder.Load(expectedEntity); // ARRANGE ASSERTIONS - transactionBuilder.IsEntityKnown().ShouldBeTrue(); + sourceBuilder.IsEntityKnown().ShouldBeTrue(); // ACT - var actualEntityId = transactionBuilder.EntityId; - var actualEntity = transactionBuilder.GetEntity(); + var actualEntityBranch = sourceBuilder.EntityId; + var actualEntity = sourceBuilder.GetEntity(); // ASSERT - actualEntityId.ShouldBe(expectedEntityId); + actualEntityBranch.ShouldBe(expectedEntityId); actualEntity.ShouldBe(expectedEntity); } private async Task - Generic_GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenTransactionDoesInsertLeases( + Generic_GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenSourceDoesInsertLeases( EntityAdder entityAdder) where TEntity : IEntity { @@ -88,26 +89,26 @@ private async Task entityAdder.AddDependencies.Invoke(serviceCollection); }); - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, default); // ACT - var transaction = transactionBuilder + var source = sourceBuilder .Append(new AddLease(new Lease(default!, default!, default!))) .Build(default); - - var entity = transactionBuilder.GetEntity(); + + var entity = sourceBuilder.GetEntity(); // ASSERT - transaction.Commands.Length.ShouldBe(1); + source.Messages.Length.ShouldBe(1); - var addLeasesCommand = - transaction.Commands[0].Data.ShouldBeAssignableTo>().ShouldNotBeNull(); + var addLeasesDelta = + source.Messages[0].Delta.ShouldBeAssignableTo>().ShouldNotBeNull(); - addLeasesCommand.GetLeases(entity).ShouldNotBeEmpty(); + addLeasesDelta.GetLeases(entity).ShouldNotBeEmpty(); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( @@ -123,12 +124,12 @@ private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_T entityAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddScoped(_ => - GetMockedTransactionRepositoryFactory( + GetMockedSourceRepositoryFactory( new object[] { new DoNothing() })); }); - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, default); await using var entityRepository = await serviceScope.ServiceProvider @@ -139,52 +140,52 @@ private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_T // ACT - transactionBuilder.Load(entity); + sourceBuilder.Load(entity); // ASSERT - Should.Throw(() => { transactionBuilder.Load(entity); }); + Should.Throw(() => { sourceBuilder.Load(entity); }); } private async Task - Generic_GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionNumberAutoIncrements( + Generic_GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionAutoIncrements( EntityAdder entityAdder) where TEntity : IEntity { // ARRANGE - var numberOfVersionsToTest = new VersionNumber(10); + var numberOfVersionsToTest = new Version(10); using var serviceScope = CreateServiceScope(serviceCollection => { entityAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddScoped(_ => - GetMockedTransactionRepositoryFactory()); + GetMockedSourceRepositoryFactory()); }); - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, default); // ACT - for (var i = new VersionNumber(1); i.Value <= numberOfVersionsToTest.Value; i = i.Next()) - transactionBuilder.Append(new DoNothing()); + for (var i = new Version(1); i.Value <= numberOfVersionsToTest.Value; i = i.Next()) + sourceBuilder.Append(new DoNothing()); - var transaction = transactionBuilder.Build(default); + var source = sourceBuilder.Build(default); // ASSERT - for (var v = new VersionNumber(1); v.Value <= numberOfVersionsToTest.Value; v = v.Next()) + for (var v = new Version(1); v.Value <= numberOfVersionsToTest.Value; v = v.Next()) { var index = (int)(v.Value - 1); - transaction.Commands[index].EntityVersionNumber.ShouldBe(v); + source.Messages[index].EntityPointer.Version.ShouldBe(v); } } - private async Task Generic_GivenExistingEntity_WhenAppendingNewCommand_ThenTransactionBuilds( + private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds( EntityAdder entityAdder) where TEntity : IEntity { @@ -197,11 +198,11 @@ private async Task Generic_GivenExistingEntity_WhenAppendingNewCommand_ThenTrans entityAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddScoped(_ => - GetMockedTransactionRepositoryFactory(new object[] { new DoNothing() })); + GetMockedSourceRepositoryFactory(new object[] { new DoNothing() })); }); - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() .CreateForSingleEntity(default!, default); await using var entityRepository = await serviceScope.ServiceProvider @@ -212,16 +213,16 @@ private async Task Generic_GivenExistingEntity_WhenAppendingNewCommand_ThenTrans // ACT - var transaction = transactionBuilder + var source = sourceBuilder .Load(entity) .Append(new DoNothing()) .Build(default); // ASSERT - transaction.Commands.Length.ShouldBe(1); + source.Messages.Length.ShouldBe(1); - transaction.Commands[0].Data.ShouldBeEquivalentTo(new DoNothing()); + source.Messages[0].Delta.ShouldBeEquivalentTo(new DoNothing()); } [Theory] @@ -248,7 +249,7 @@ public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(EntityAd [Theory] [MemberData(nameof(AddEntity))] - public Task GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenTransactionDoesInsertLeases( + public Task GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenSourceDoesInsertLeases( EntityAdder entityAdder) { return RunGenericTestAsync @@ -271,7 +272,7 @@ public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(E [Theory] [MemberData(nameof(AddEntity))] - public Task GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionNumberAutoIncrements( + public Task GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionAutoIncrements( EntityAdder entityAdder) { return RunGenericTestAsync @@ -283,7 +284,7 @@ public Task GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersio [Theory] [MemberData(nameof(AddEntity))] - public Task GivenExistingEntity_WhenAppendingNewCommand_ThenTransactionBuilds(EntityAdder entityAdder) + public Task GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(EntityAdder entityAdder) { return RunGenericTestAsync ( diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs new file mode 100644 index 00000000..ec8fe39e --- /dev/null +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -0,0 +1,1757 @@ +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Entities; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Agents; +using EntityDb.Common.Sources.Attributes; +using EntityDb.Common.Sources.Queries; +using EntityDb.Common.Sources.Queries.Modified; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.Common.Tests.Implementations.Deltas; +using EntityDb.Common.Tests.Implementations.Leases; +using EntityDb.Common.Tests.Implementations.Queries; +using EntityDb.Common.Tests.Implementations.Seeders; +using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.Tags; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using Shouldly; +using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.Sources; + +[Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class SourceTests : TestsBase +{ + public SourceTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) + : base(startupServiceProvider, databaseContainerFixture) + { + } + + private static async Task PutSources + ( + IServiceScope serviceScope, + List sources + ) + { + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + foreach (var source in sources) + { + var sourceCommitted = await sourceRepository.Commit(source); + + sourceCommitted.ShouldBeTrue(); + } + } + + private static ModifiedQueryOptions NewModifiedQueryOptions(bool invertFilter, bool reverseSort, int? replaceSkip, + int? replaceTake) + { + return new ModifiedQueryOptions + { + InvertFilter = invertFilter, + ReverseSort = reverseSort, + ReplaceSkip = replaceSkip, + ReplaceTake = replaceTake, + }; + } + + private static async Task TestGet + ( + IServiceScope serviceScope, + Func getExpectedResults, + Func> getActualResults, + bool secondaryPreferred + ) + { + // ARRANGE + + var bufferModifier = NewModifiedQueryOptions(false, false, null, null); + var negateModifier = NewModifiedQueryOptions(true, false, null, null); + var reverseBufferModifier = NewModifiedQueryOptions(false, true, null, null); + var reverseNegateModifier = NewModifiedQueryOptions(true, true, null, null); + var bufferSubsetModifier = NewModifiedQueryOptions(false, false, 1, 1); + + var expectedTrueResults = getExpectedResults.Invoke(false); + var expectedFalseResults = getExpectedResults.Invoke(true); + var reversedExpectedTrueResults = expectedTrueResults.Reverse().ToArray(); + var reversedExpectedFalseResults = expectedFalseResults.Reverse().ToArray(); + var expectedSkipTakeResults = expectedTrueResults.Skip(1).Take(1).ToArray(); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(secondaryPreferred + ? TestSessionOptions.ReadOnlySecondaryPreferred + : TestSessionOptions.ReadOnly); + + // ACT + + var actualTrueResults = + await getActualResults.Invoke(sourceRepository, bufferModifier).ToArrayAsync(); + var actualFalseResults = + await getActualResults.Invoke(sourceRepository, negateModifier).ToArrayAsync(); + var reversedActualTrueResults = + await getActualResults.Invoke(sourceRepository, reverseBufferModifier).ToArrayAsync(); + var reversedActualFalseResults = + await getActualResults.Invoke(sourceRepository, reverseNegateModifier).ToArrayAsync(); + var actualSkipTakeResults = + await getActualResults.Invoke(sourceRepository, bufferSubsetModifier).ToArrayAsync(); + + // ASSERT + + actualTrueResults.ShouldBeEquivalentTo(expectedTrueResults); + actualFalseResults.ShouldBeEquivalentTo(expectedFalseResults); + reversedActualTrueResults.ShouldBeEquivalentTo(reversedExpectedTrueResults); + reversedActualFalseResults.ShouldBeEquivalentTo(reversedExpectedFalseResults); + actualSkipTakeResults.ShouldBeEquivalentTo(expectedSkipTakeResults); + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + IMessageGroupQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseSourceIds + : expectedObjects.TrueSourceIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + IMessageQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + } + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseSourceIds + : expectedObjects.TrueSourceIds) + .ToArray(); + } + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + ILeaseQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseSourceIds + : expectedObjects.TrueSourceIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + ITagQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseSourceIds + : expectedObjects.TrueSourceIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetEntityIds + ( + IServiceScope serviceScope, + IMessageGroupQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseEntityIds + : expectedObjects.TrueEntityIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .Select(pointer => pointer.Id); + } + } + + private static async Task TestGetEntityIds + ( + IServiceScope serviceScope, + IMessageQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseEntityIds + : expectedObjects.TrueEntityIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .Select(pointer => pointer.Id); + } + } + + private static async Task TestGetEntityIds + ( + IServiceScope serviceScope, + ILeaseQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseEntityIds + : expectedObjects.TrueEntityIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .Select(pointer => pointer.Id); + } + } + + private static async Task TestGetEntityIds + ( + IServiceScope serviceScope, + ITagQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseEntityIds + : expectedObjects.TrueEntityIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .Select(pointer => pointer.Id); + } + } + + private static async Task TestGetAgentSignatures + ( + IServiceScope serviceScope, + IMessageGroupQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + object[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseAgentSignatures + : expectedObjects.TrueAgentSignatures) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateAgentSignatures(query.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetDeltas + ( + IServiceScope serviceScope, + IMessageQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + object[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseDeltas + : expectedObjects.TrueDeltas) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateDeltas(query.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetLeases + ( + IServiceScope serviceScope, + ILeaseQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + ILease[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseLeases + : expectedObjects.TrueLeases) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateLeases(query.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetTags + ( + IServiceScope serviceScope, + ITagQuery query, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateTags(query.Modify(modifiedQueryOptions)); + } + + ITag[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseTags + : expectedObjects.TrueTags) + .ToArray(); + } + } + + private static async Task BuildSource + ( + IServiceScope serviceScope, + Id sourceId, + Id entityId, + IEnumerable counts, + TimeStamp? timeStampOverride = null, + object? agentSignatureOverride = null + ) + where TEntity : IEntity + { + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, entityId); + + foreach (var count in counts) sourceBuilder.Append(new StoreNumber(count)); + + var source = sourceBuilder.Build(sourceId).ShouldBeOfType(); + + if (timeStampOverride.HasValue) + source = source with + { + TimeStamp = timeStampOverride.Value, + }; + + if (agentSignatureOverride is not null) + source = source with + { + AgentSignature = agentSignatureOverride, + }; + + return source; + } + + private static Id[] GetSortedIds(int numberOfIds) + { + return Enumerable + .Range(1, numberOfIds) + .Select(_ => Id.NewId()) + .OrderBy(id => id.Value) + .ToArray(); + } + + private async Task + Generic_GivenReadOnlyMode_WhenCommittingSource_ThenCannotWriteInReadOnlyModeExceptionIsLogged( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.RemoveAll(typeof(ILoggerFactory)); + + serviceCollection.AddSingleton(loggerFactory); + }); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var source = sourceBuilder + .Append(DeltaSeeder.Create()) + .Build(default); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.ReadOnly); + + // ACT + + var inserted = await sourceRepository.Commit(source); + + // ASSERT + + inserted.ShouldBeFalse(); + + loggerVerifier.Invoke(Times.Once()); + } + + private async Task Generic_GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sourceId = Id.NewId(); + + var firstSourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var secondSourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var firstSource = firstSourceBuilder + .Append(DeltaSeeder.Create()) + .Build(sourceId); + + var secondSource = secondSourceBuilder + .Append(DeltaSeeder.Create()) + .Build(sourceId); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + // ACT + + var firstSourceCommitted = await sourceRepository.Commit(firstSource); + var secondSourceCommitted = await sourceRepository.Commit(secondSource); + + // ASSERT + + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeFalse(); + } + + private async Task Generic_GivenNonUniqueVersions_WhenInsertingDeltas_ThenReturnFalse( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + const int repeatCount = 2; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var source = sourceBuilder + .Append(DeltaSeeder.Create()) + .Build(default) + .ShouldBeOfType(); + + source = source with + { + Messages = Enumerable + .Repeat(source.Messages, repeatCount) + .SelectMany(messages => messages) + .ToImmutableArray(), + }; + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + // ARRANGE ASSERTIONS + + repeatCount.ShouldBeGreaterThan(1); + + // ACT + + var sourceCommitted = await sourceRepository.Commit(source); + + // ASSERT + + sourceCommitted.ShouldBeFalse(); + } + + private async Task + Generic_GivenVersionZero_WhenInsertingDeltas_ThenSourceIsCommitted( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var message = new Message + { + EntityPointer = Id.NewId() + Version.Zero, + Delta = new DoNothing(), + }; + + var source = SourceSeeder.Create(message); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + // ACT + + var sourceCommitted = + await sourceRepository.Commit(source); + + // ASSERT + + sourceCommitted.ShouldBeTrue(); + } + + private async Task + Generic_GivenNonUniqueVersions_WhenInsertingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.RemoveAll(typeof(ILoggerFactory)); + + serviceCollection.AddSingleton(loggerFactory); + }); + + var entityId = Id.NewId(); + + var firstSourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, entityId); + + var secondSourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, entityId); + + var firstSource = firstSourceBuilder + .Append(DeltaSeeder.Create()) + .Build(Id.NewId()); + + var secondSource = secondSourceBuilder + .Append(DeltaSeeder.Create()) + .Build(Id.NewId()); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + // ACT + + var firstSourceCommitted = + await sourceRepository.Commit(firstSource); + var secondSourceCommitted = + await sourceRepository.Commit(secondSource); + + // ASSERT + + firstSource.Messages.Length.ShouldBe(1); + secondSource.Messages.Length.ShouldBe(1); + + firstSource.Messages.ShouldAllBe(message => message.EntityPointer.Id == entityId); + secondSource.Messages.ShouldAllBe(message => message.EntityPointer.Id == entityId); + + firstSource.Messages[0].EntityPointer.Version + .ShouldBe(secondSource.Messages[0].EntityPointer.Version); + + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeFalse(); + + loggerVerifier.Invoke(Times.Once()); + } + + private async Task Generic_GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var tag = TagSeeder.Create(); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var source = sourceBuilder + .Append(new AddTag(tag)) + .Append(new AddTag(tag)) + .Build(default); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + // ACT + + var sourceCommitted = await sourceRepository.Commit(source); + + // ASSERT + + sourceCommitted.ShouldBeTrue(); + } + + private async Task Generic_GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFalse( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var lease = LeaseSeeder.Create(); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var source = sourceBuilder + .Append(new AddLease(lease)) + .Append(new AddLease(lease)) + .Build(default); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + // ACT + + var sourceCommitted = await sourceRepository.Commit(source); + + // ASSERT + + sourceCommitted.ShouldBeFalse(); + } + + private async Task + Generic_GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + const ulong expectedCount = 5; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var expectedSourceId = Id.NewId(); + var expectedEntityId = Id.NewId(); + var expectedSourceTimeStamp = sourcesAdder.FixTimeStamp(TimeStamp.UtcNow); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + var source = await BuildSource(serviceScope, expectedSourceId, expectedEntityId, + new[] { expectedCount }, expectedSourceTimeStamp, agentSignature); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + var sourceCommitted = await sourceRepository.Commit(source); + + var query = new EntityBranchQuery(expectedEntityId); + + // ARRANGE ASSERTIONS + + sourceCommitted.ShouldBeTrue(); + + // ACT + + var annotatedAgentSignatures = + await sourceRepository.EnumerateAnnotatedAgentSignatures(query).ToArrayAsync(); + + // ASSERT + + annotatedAgentSignatures.Length.ShouldBe(1); + + annotatedAgentSignatures[0].SourceId.ShouldBe(expectedSourceId); + annotatedAgentSignatures[0].SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); + annotatedAgentSignatures[0].EntityPointers.Length.ShouldBe(1); + annotatedAgentSignatures[0].EntityPointers[0].Id.ShouldBe(expectedEntityId); + annotatedAgentSignatures[0].Data.ShouldBeEquivalentTo(agentSignature); + } + + private async Task Generic_GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + const ulong expectedDeltaCount = 5; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var expectedSourceId = Id.NewId(); + var expectedEntityId = Id.NewId(); + var expectedSourceTimeStamp = sourcesAdder.FixTimeStamp(TimeStamp.UtcNow); + + var source = await BuildSource(serviceScope, expectedSourceId, expectedEntityId, + new[] { expectedDeltaCount }, expectedSourceTimeStamp); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + var sourceCommitted = await sourceRepository.Commit(source); + + var query = new GetDeltasQuery(expectedEntityId, default); + + // ARRANGE ASSERTIONS + + sourceCommitted.ShouldBeTrue(); + + // ACT + + var annotatedDeltas = await sourceRepository.EnumerateAnnotatedDeltas(query).ToArrayAsync(); + + // ASSERT + + annotatedDeltas.Length.ShouldBe(1); + + annotatedDeltas[0].SourceId.ShouldBe(expectedSourceId); + annotatedDeltas[0].SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); + annotatedDeltas[0].EntityPointer.Id.ShouldBe(expectedEntityId); + annotatedDeltas[0].EntityPointer.Version.ShouldBe(new Version(1)); + + var actualDeltaCount = annotatedDeltas[0].Data.ShouldBeAssignableTo().ShouldNotBeNull(); + + actualDeltaCount.Number.ShouldBe(expectedDeltaCount); + } + + private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : class, IEntity, ISnapshotWithTestLogic + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var entityId = Id.NewId(); + + var expectedEntity = TEntity.Construct(entityId).WithVersion(new Version(1)); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + var entityRepository = EntityRepository.Create(serviceScope.ServiceProvider, sourceRepository); + + var source = await BuildSource(serviceScope, Id.NewId(), entityId, + new[] { 0UL }); + + var sourceCommitted = await sourceRepository.Commit(source); + + // ARRANGE ASSERTIONS + + sourceCommitted.ShouldBeTrue(); + + // ACT + + var actualEntity = await entityRepository.GetSnapshot(entityId); + + // ASSERT + + actualEntity.ShouldBeEquivalentTo(expectedEntity); + } + + private async Task Generic_GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var entityId = Id.NewId(); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, entityId); + + var tag = new Tag("Foo", "Bar"); + + var expectedInitialTags = new[] { tag }.ToImmutableArray(); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + var initialSource = sourceBuilder + .Append(new AddTag(tag)) + .Build(Id.NewId()); + + var initialSourceCommitted = await sourceRepository.Commit(initialSource); + + var tagQuery = new DeleteTagsQuery(entityId, expectedInitialTags); + + // ARRANGE ASSERTIONS + + initialSourceCommitted.ShouldBeTrue(); + + // ACT + + var actualInitialTags = await sourceRepository.EnumerateTags(tagQuery).ToArrayAsync(); + + var finalSource = sourceBuilder + .Append(new DeleteTag(tag)) + .Build(Id.NewId()); + + var finalSourceCommitted = await sourceRepository.Commit(finalSource); + + var actualFinalTags = await sourceRepository.EnumerateTags(tagQuery).ToArrayAsync(); + + // ASSERT + + finalSourceCommitted.ShouldBeTrue(); + + expectedInitialTags.SequenceEqual(actualInitialTags).ShouldBeTrue(); + + actualFinalTags.ShouldBeEmpty(); + } + + private async Task Generic_GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var entityId = Id.NewId(); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, entityId); + + var lease = new Lease("Foo", "Bar", "Baz"); + + var expectedInitialLeases = new[] { lease }.ToImmutableArray(); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + var initialSource = sourceBuilder + .Append(new AddLease(lease)) + .Build(Id.NewId()); + + var initialSourceCommitted = await sourceRepository.Commit(initialSource); + + var leaseQuery = new DeleteLeasesQuery(expectedInitialLeases); + + // ARRANGE ASSERTIONS + + initialSourceCommitted.ShouldBeTrue(); + + // ACT + + var actualInitialLeases = await sourceRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); + + var finalSource = sourceBuilder + .Append(new DeleteLease(lease)) + .Build(Id.NewId()); + + var finalSourceCommitted = await sourceRepository.Commit(finalSource); + + var actualFinalLeases = await sourceRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); + + // ASSERT + + finalSourceCommitted.ShouldBeTrue(); + + actualInitialLeases.SequenceEqual(expectedInitialLeases).ShouldBeTrue(); + + actualFinalLeases.ShouldBeEmpty(); + } + + private async Task + Generic_GivenSourceCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var expectedDelta = new StoreNumber(1); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var source = sourceBuilder + .Append(expectedDelta) + .Build(Id.NewId()); + + var versionOneQuery = new EntityVersionQuery(new Version(1), new Version(1)); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(TestSessionOptions.Write); + + // ACT + + var sourceCommitted = await sourceRepository.Commit(source); + + var newCommands = await sourceRepository.EnumerateDeltas(versionOneQuery).ToArrayAsync(); + + // ASSERT + + sourceCommitted.ShouldBeTrue(); + + source.Messages.Length.ShouldBe(1); + + source.Messages[0].EntityPointer.Version.ShouldBe(new Version(1)); + + newCommands.Length.ShouldBe(1); + + newCommands[0].ShouldBeEquivalentTo(expectedDelta); + } + + private async Task + Generic_GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedDelta< + TEntity>(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var expectedDelta = new StoreNumber(2); + + var sourceBuilder = await serviceScope.ServiceProvider + .GetRequiredService>() + .CreateForSingleEntity(default!, default); + + var firstSource = sourceBuilder + .Append(new StoreNumber(1)) + .Build(Id.NewId()); + + var secondSource = sourceBuilder + .Append(expectedDelta) + .Build(Id.NewId()); + + var versionTwoQuery = new EntityVersionQuery(new Version(2), new Version(2)); + + await using var sourceRepository = await serviceScope.ServiceProvider + .GetRequiredService().CreateRepository(TestSessionOptions.Write); + + var firstSourceCommitted = await sourceRepository.Commit(firstSource); + + // ARRANGE ASSERTIONS + + firstSourceCommitted.ShouldBeTrue(); + + // ACT + + var secondSourceCommitted = await sourceRepository.Commit(secondSource); + + var newCommands = await sourceRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); + + // ASSERT + + secondSourceCommitted.ShouldBeTrue(); + + secondSource.Messages.Length.ShouldBe(1); + + secondSource.Messages[0].EntityPointer.Version.ShouldBe(new Version(2)); + + newCommands.Length.ShouldBe(1); + + newCommands[0].ShouldBeEquivalentTo(expectedDelta); + } + + private async Task + Generic_GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + const ulong timeSpanInMinutes = 60UL; + const ulong gteInMinutes = 20UL; + const ulong lteInMinutes = 30UL; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var originTimeStamp = TimeStamp.UnixEpoch; + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + var sourceIds = GetSortedIds((int)timeSpanInMinutes); + var entityIds = GetSortedIds((int)timeSpanInMinutes); + + TimeStamp? gte = null; + TimeStamp? lte = null; + + for (var i = 1UL; i <= timeSpanInMinutes; i++) + { + var currentSourceId = sourceIds[i - 1]; + var currentEntityId = entityIds[i - 1]; + var currentSourceTimeStamp = new TimeStamp(originTimeStamp.Value.AddMinutes(i)); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + var deltas = new object[] { new StoreNumber(i) }; + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + expectedObjects.Add(i is >= gteInMinutes and <= lteInMinutes, currentSourceId, currentEntityId, + agentSignature, deltas, leases, tags); + + switch (i) + { + case lteInMinutes: + lte = currentSourceTimeStamp; + break; + + case gteInMinutes: + gte = currentSourceTimeStamp; + break; + } + + var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, + new[] { i }, + currentSourceTimeStamp, agentSignature); + + sources.Add(source); + } + + gte.ShouldNotBeNull(); + lte.ShouldNotBeNull(); + + var query = new SourceTimeStampQuery(gte.Value, lte.Value); + + await PutSources(serviceScope, sources); + await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as IMessageGroupQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetAgentSignatures(serviceScope, query, expectedObjects); + await TestGetDeltas(serviceScope, query, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + private async Task + Generic_GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + const ulong numberOfSourceIds = 10UL; + const ulong whichSourceId = 5UL; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + Id? sourceId = null; + + var sourceIds = GetSortedIds((int)numberOfSourceIds); + var entityIds = GetSortedIds((int)numberOfSourceIds); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + for (var i = 1UL; i <= numberOfSourceIds; i++) + { + var currentSourceId = sourceIds[i - 1]; + var currentEntityId = entityIds[i - 1]; + + var deltas = new object[] { new StoreNumber(i) }; + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + expectedObjects.Add(i == whichSourceId, currentSourceId, currentEntityId, agentSignature, + deltas, + leases, tags); + + if (i == whichSourceId) sourceId = currentSourceId; + + var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, + new[] { i }, + agentSignatureOverride: agentSignature); + + sources.Add(source); + } + + sourceId.ShouldNotBeNull(); + + var query = new SourceIdQuery(sourceId.Value); + + await PutSources(serviceScope, sources); + await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as IMessageGroupQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetAgentSignatures(serviceScope, query, expectedObjects); + await TestGetDeltas(serviceScope, query, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + private async Task + Generic_GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + const ulong numberOfEntityIds = 10UL; + const ulong whichEntityId = 5UL; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + Id? entityId = null; + + var sourceIds = GetSortedIds((int)numberOfEntityIds); + var entityIds = GetSortedIds((int)numberOfEntityIds); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + for (var i = 1UL; i <= numberOfEntityIds; i++) + { + var currentSourceId = sourceIds[i - 1]; + var currentEntityId = entityIds[i - 1]; + + var deltas = new object[] { new StoreNumber(i) }; + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + expectedObjects.Add(i == whichEntityId, currentSourceId, currentEntityId, agentSignature, deltas, + leases, tags); + + if (i == whichEntityId) entityId = currentEntityId; + + var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, + new[] { i }, + agentSignatureOverride: agentSignature); + + sources.Add(source); + } + + entityId.ShouldNotBeNull(); + + var query = new EntityBranchQuery(entityId.Value); + + await PutSources(serviceScope, sources); + await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as IMessageGroupQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetAgentSignatures(serviceScope, query, expectedObjects); + await TestGetDeltas(serviceScope, query, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + private async Task + Generic_GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + const ulong numberOfVersions = 20; + const ulong gte = 5UL; + const ulong lte = 15UL; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var counts = new List(); + var expectedObjects = new ExpectedObjects(); + + for (var i = 1UL; i <= numberOfVersions; i++) + { + var delta = new StoreNumber(i); + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + counts.Add(i); + + expectedObjects.Add(i is >= gte and <= lte, default, default, default!, new[] { delta }, + leases, tags); + } + + var source = await BuildSource(serviceScope, Id.NewId(), Id.NewId(), counts.ToArray()); + + var sources = new List { source }; + + var query = new EntityVersionQuery(new Version(gte), new Version(lte)); + + await PutSources(serviceScope, sources); + await TestGetDeltas(serviceScope, query, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TOptions : class + where TEntity : IEntity + { + const ulong countTo = 20UL; + const ulong gte = 5UL; + const ulong lte = 15UL; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + var sourceIds = GetSortedIds((int)countTo); + var entityIds = GetSortedIds((int)countTo); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + var deltas = new object[] { new DoNothing() }; + + for (var i = 1UL; i <= countTo; i++) + { + var currentSourceId = sourceIds[i - 1]; + var currentEntityId = entityIds[i - 1]; + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + expectedObjects.Add(i is >= gte and <= lte, currentSourceId, currentEntityId, agentSignature, deltas, + leases, tags); + + var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, + new[] { i }, + agentSignatureOverride: agentSignature); + + sources.Add(source); + } + + var options = serviceScope.ServiceProvider + .GetRequiredService>() + .Create("Count"); + + var query = new CountQuery(gte, lte, options); + + await PutSources(serviceScope, sources); + await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenReadOnlyMode_WhenCommittingSource_ThenCannotWriteInReadOnlyModeExceptionIsLogged( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenNonUniqueVersions_WhenInsertingDeltas_ThenReturnFalse(SourcesAdder sourcesAdder, + EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenVersionZero_WhenInsertingDeltas_ThenSourceIsCommitted( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenNonUniqueVersions_WhenInsertingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue(SourcesAdder sourcesAdder, + EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFalse(SourcesAdder sourcesAdder, + EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity(SourcesAdder sourcesAdder, + EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedDelta( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { sourcesAdder.QueryOptionsType, entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + private class ExpectedObjects + { + public readonly List FalseAgentSignatures = new(); + public readonly List FalseDeltas = new(); + public readonly List FalseEntityIds = new(); + public readonly List FalseLeases = new(); + public readonly List FalseSourceIds = new(); + public readonly List FalseTags = new(); + + public readonly List TrueAgentSignatures = new(); + public readonly List TrueDeltas = new(); + public readonly List TrueEntityIds = new(); + public readonly List TrueLeases = new(); + public readonly List TrueSourceIds = new(); + public readonly List TrueTags = new(); + + public void Add + ( + bool condition, + Id sourceId, + Id entityId, + object agentSignature, + IEnumerable deltas, + IEnumerable leases, + IEnumerable tags + ) + { + if (condition) + { + TrueSourceIds.Add(sourceId); + TrueEntityIds.Add(entityId); + TrueAgentSignatures.Add(agentSignature); + TrueDeltas.AddRange(deltas); + TrueLeases.AddRange(leases); + TrueTags.AddRange(tags); + } + else + { + FalseSourceIds.Add(sourceId); + FalseEntityIds.Add(entityId); + FalseAgentSignatures.Add(agentSignature); + FalseDeltas.AddRange(deltas); + FalseLeases.AddRange(leases); + FalseTags.AddRange(tags); + } + } + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs new file mode 100644 index 00000000..925102e4 --- /dev/null +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -0,0 +1,149 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Common.Sources; +using Microsoft.Extensions.Logging; +using Moq; +using Shouldly; +using Xunit; + +namespace EntityDb.Common.Tests.Sources; + +public class TryCatchSourceRepositoryTests : TestsBase +{ + public TryCatchSourceRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + [Fact] + public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged() + { + // ARRANGE + + var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => repository.EnumerateLeases(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => repository.EnumerateTags(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), + It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.Commit(It.IsAny(), It.IsAny())) + .ThrowsAsync(new NotImplementedException()); + + var tryCatchSourceRepository = new TryCatchSourceRepository(sourceRepositoryMock.Object, + loggerFactory.CreateLogger()); + + // ACT + + var sourceIdsFromMessageGroupQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageGroupQuery)!).ToArrayAsync(); + var sourceIdsFromMessageQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageQuery)!).ToArrayAsync(); + var sourceIdsFromLeaseQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseQuery)!).ToArrayAsync(); + var sourceIdsFromTagQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ITagQuery)!).ToArrayAsync(); + var entityPointersFromMessageGroupQuery = + await tryCatchSourceRepository.EnumerateEntityPointers(default(IMessageGroupQuery)!).ToArrayAsync(); + var entityPointersFromMessageQuery = + await tryCatchSourceRepository.EnumerateEntityPointers(default(IMessageQuery)!).ToArrayAsync(); + var entityPointersFromLeaseQuery = + await tryCatchSourceRepository.EnumerateEntityPointers(default(ILeaseQuery)!).ToArrayAsync(); + var entityPointersFromTagQuery = + await tryCatchSourceRepository.EnumerateEntityPointers(default(ITagQuery)!).ToArrayAsync(); + var agentSignatures = + await tryCatchSourceRepository.EnumerateAgentSignatures(default!).ToArrayAsync(); + var deltas = + await tryCatchSourceRepository.EnumerateDeltas(default!).ToArrayAsync(); + var leases = + await tryCatchSourceRepository.EnumerateLeases(default!).ToArrayAsync(); + var tags = + await tryCatchSourceRepository.EnumerateTags(default!).ToArrayAsync(); + var annotatedDeltas = + await tryCatchSourceRepository.EnumerateAnnotatedDeltas(default!).ToArrayAsync(); + var inserted = + await tryCatchSourceRepository.Commit(default!); + + // ASSERT + + sourceIdsFromMessageGroupQuery.ShouldBeEmpty(); + sourceIdsFromMessageQuery.ShouldBeEmpty(); + sourceIdsFromLeaseQuery.ShouldBeEmpty(); + sourceIdsFromTagQuery.ShouldBeEmpty(); + entityPointersFromMessageGroupQuery.ShouldBeEmpty(); + entityPointersFromMessageQuery.ShouldBeEmpty(); + entityPointersFromLeaseQuery.ShouldBeEmpty(); + entityPointersFromTagQuery.ShouldBeEmpty(); + agentSignatures.ShouldBeEmpty(); + deltas.ShouldBeEmpty(); + leases.ShouldBeEmpty(); + tags.ShouldBeEmpty(); + annotatedDeltas.ShouldBeEmpty(); + inserted.ShouldBeFalse(); + loggerVerifier.Invoke(Times.Exactly(14)); + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/StartupBase.cs b/test/EntityDb.Common.Tests/StartupBase.cs index 2146b011..f3229dfb 100644 --- a/test/EntityDb.Common.Tests/StartupBase.cs +++ b/test/EntityDb.Common.Tests/StartupBase.cs @@ -1,5 +1,5 @@ -using EntityDb.Common.Agents; -using EntityDb.Common.Extensions; +using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Agents; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.Common.Tests; @@ -20,7 +20,7 @@ public virtual void AddServices(IServiceCollection serviceCollection) serviceCollection.AddAgentAccessor(); - // Transaction Processor Queue + // Source Processor Queue serviceCollection.AddSourceProcessorQueue(true); } diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index f616eecd..2b1eae64 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -1,28 +1,22 @@ using System.Diagnostics; using System.Reflection; -using EntityDb.Abstractions.Queries; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; using EntityDb.Common.Extensions; using EntityDb.Common.Polyfills; -using EntityDb.Common.Projections; -using EntityDb.Common.Tests.Implementations.DbContexts; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.EntityFramework.Extensions; -using EntityDb.EntityFramework.Sessions; using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Queries; using EntityDb.MongoDb.Snapshots.Sessions; -using EntityDb.MongoDb.Transactions.Sessions; -using EntityDb.Npgsql.Extensions; -using EntityDb.Npgsql.Queries; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Sessions; using EntityDb.Redis.Extensions; -using EntityDb.Redis.Sessions; -using EntityDb.SqlDb.Sessions; +using EntityDb.Redis.Snapshots.Sessions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -41,97 +35,61 @@ public class TestsBase where TStartup : IStartup, new() { public delegate void AddDependenciesDelegate(IServiceCollection serviceCollection); - - private static readonly TransactionsAdder[] AllTransactionAdders = + + private static readonly SourcesAdder[] AllSourceAdders = { - new("MongoDb", typeof(MongoDbQueryOptions), (timeStamp) => timeStamp.WithMillisecondPrecision(), serviceCollection => - { - var databaseContainerFixture = serviceCollection - .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) - .ImplementationInstance as DatabaseContainerFixture; + new("MongoDb", typeof(MongoDbQueryOptions), timeStamp => timeStamp.WithMillisecondPrecision(), + serviceCollection => + { + var databaseContainerFixture = (serviceCollection + .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) + .ImplementationInstance as DatabaseContainerFixture)!; - serviceCollection.AddMongoDbTransactions(true, true); + serviceCollection.AddMongoDbSources(true, true); - serviceCollection.Configure("Count", options => - { - options.FindOptions = new FindOptions + serviceCollection.Configure("Count", options => { - Collation = new Collation("en", numericOrdering: true) - }; - }); - - serviceCollection.ConfigureAll(options => - { - var host = databaseContainerFixture!.MongoDbContainer.Hostname; - var port = databaseContainerFixture!.MongoDbContainer.GetMappedPublicPort(27017); + options.FindOptions = new FindOptions + { + Collation = new Collation("en", numericOrdering: true), + }; + }); - options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); - options.DatabaseName = DatabaseContainerFixture.OmniParameter; - options.WriteTimeout = TimeSpan.FromSeconds(1); - }); + serviceCollection.ConfigureAll(options => + { + var host = databaseContainerFixture.MongoDbContainer.Hostname; + var port = databaseContainerFixture.MongoDbContainer.GetMappedPublicPort(27017); - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); + options.DatabaseName = DatabaseContainerFixture.OmniParameter; + options.WriteTimeout = TimeSpan.FromSeconds(1); + }); - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = false; - }); + serviceCollection.Configure(TestSessionOptions.Write, + options => { options.ReadOnly = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = true; - }); - }), - - new("Npgsql", typeof(NpgsqlQueryOptions), (timeStamp) => timeStamp.WithMicrosecondPrecision(), serviceCollection => - { - var databaseContainerFixture = serviceCollection - .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) - .ImplementationInstance as DatabaseContainerFixture; - - serviceCollection.AddNpgsqlTransactions(true, true); - - serviceCollection.Configure("Count", options => - { - options.LeaseValueSortCollation = "numeric"; - options.TagValueSortCollation = "numeric"; - }); - - serviceCollection.ConfigureAll(options => - { - options.ConnectionString = $"{databaseContainerFixture!.PostgreSqlContainer.GetConnectionString()};Include Error Detail=true"; - }); - - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); - - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = false; - }); - - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = true; - }); - }) + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => + { + options.ReadOnly = true; + options.SecondaryPreferred = false; + }); + + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + options => + { + options.ReadOnly = true; + options.SecondaryPreferred = true; + }); + }), }; private readonly IConfiguration _configuration; + private readonly DatabaseContainerFixture? _databaseContainerFixture; private readonly ITest _test; private readonly ITestOutputHelperAccessor _testOutputHelperAccessor; - private readonly DatabaseContainerFixture? _databaseContainerFixture; - protected TestsBase(IServiceProvider startupServiceProvider, DatabaseContainerFixture? databaseContainerFixture = null) + protected TestsBase(IServiceProvider startupServiceProvider, + DatabaseContainerFixture? databaseContainerFixture = null) { _configuration = startupServiceProvider.GetRequiredService(); _testOutputHelperAccessor = startupServiceProvider.GetRequiredService(); @@ -155,54 +113,21 @@ protected Task RunGenericTestAsync(Type[] typeArguments, object?[] invokeParamet .ShouldNotBeNull(); } - private static SnapshotAdder EntityFrameworkSnapshotAdder() - where TSnapshot : class, ISnapshotWithTestLogic - { - return new SnapshotAdder($"EntityFramework<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => - { - var databaseContainerFixture = serviceCollection - .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) - .ImplementationInstance as DatabaseContainerFixture; - - serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); - - serviceCollection.ConfigureAll(options => - { - options.ConnectionString = databaseContainerFixture!.PostgreSqlContainer.GetConnectionString(); - }); - - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); - - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => - { - options.ReadOnly = true; - }); - - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - }); - }); - } - private static SnapshotAdder MongoDbSnapshotAdder() where TSnapshot : class, ISnapshotWithTestLogic { return new SnapshotAdder($"MongoDb<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => { - var databaseContainerFixture = serviceCollection + var databaseContainerFixture = (serviceCollection .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) - .ImplementationInstance as DatabaseContainerFixture; + .ImplementationInstance as DatabaseContainerFixture)!; serviceCollection.AddMongoDbSnapshots(true, true); serviceCollection.ConfigureAll(options => { - var host = databaseContainerFixture!.MongoDbContainer.Hostname; - var port = databaseContainerFixture!.MongoDbContainer.GetMappedPublicPort(27017); + var host = databaseContainerFixture.MongoDbContainer.Hostname; + var port = databaseContainerFixture.MongoDbContainer.GetMappedPublicPort(27017); options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); options.DatabaseName = DatabaseContainerFixture.OmniParameter; @@ -210,10 +135,8 @@ private static SnapshotAdder MongoDbSnapshotAdder() options.WriteTimeout = TimeSpan.FromSeconds(1); }); - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + serviceCollection.Configure(TestSessionOptions.Write, + options => { options.ReadOnly = false; }); serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { @@ -221,11 +144,12 @@ private static SnapshotAdder MongoDbSnapshotAdder() options.SecondaryPreferred = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = true; - }); + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + options => + { + options.ReadOnly = true; + options.SecondaryPreferred = true; + }); }); } @@ -246,10 +170,8 @@ private static SnapshotAdder RedisSnapshotAdder() options.KeyNamespace = TSnapshot.RedisKeyNamespace; }); - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + serviceCollection.Configure(TestSessionOptions.Write, + options => { options.ReadOnly = false; }); serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { @@ -257,18 +179,18 @@ private static SnapshotAdder RedisSnapshotAdder() options.SecondaryPreferred = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = true; - }); + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + options => + { + options.ReadOnly = true; + options.SecondaryPreferred = true; + }); }); } private static IEnumerable AllSnapshotAdders() where TSnapshot : class, ISnapshotWithTestLogic { - yield return EntityFrameworkSnapshotAdder(); yield return RedisSnapshotAdder(); yield return MongoDbSnapshotAdder(); } @@ -286,12 +208,11 @@ private static IEnumerable AllEntitySnapshotAdders() return from snapshotAdder in AllSnapshotAdders() let entityAdder = GetEntityAdder() - select new SnapshotAdder(snapshotAdder.Name, snapshotAdder.SnapshotType, - snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => + select snapshotAdder with { AddDependencies = snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => { serviceCollection.AddEntitySnapshotSourceSubscriber(TestSessionOptions.ReadOnly, TestSessionOptions.Write); - })); + }) }; } private static IEnumerable AllEntityAdders() @@ -309,13 +230,11 @@ private static IEnumerable AllProjectionAdders() where TProjection : class, IProjection, ISnapshotWithTestLogic { return AllSnapshotAdders() - .Select(snapshotAdder => new SnapshotAdder(snapshotAdder.Name, snapshotAdder.SnapshotType, - snapshotAdder.AddDependencies + (serviceCollection => + .Select(snapshotAdder => snapshotAdder with { AddDependencies = snapshotAdder.AddDependencies + (serviceCollection => { serviceCollection.AddProjection(); - serviceCollection.AddProjectionSnapshotSourceSubscriber( - TestSessionOptions.ReadOnly, TestSessionOptions.Write); - })) + serviceCollection.AddProjectionSnapshotSourceSubscriber(TestSessionOptions.Write); + }) } ); } @@ -325,44 +244,44 @@ private static IEnumerable AllProjectionSnapshotAdders() .Concat(AllProjectionAdders()); } - public static IEnumerable AddTransactionsAndEntity() + public static IEnumerable AddSourcesAndEntity() { - return from transactionAdder in AllTransactionAdders - from entityAdder in AllEntityAdders() - select new object[] { transactionAdder, entityAdder }; + return from sourceAdder in AllSourceAdders + from entityAdder in AllEntityAdders() + select new object[] { sourceAdder, entityAdder }; } public static IEnumerable AddEntity() { return from entityAdder in AllEntityAdders() - select new object[] { entityAdder }; + select new object[] { entityAdder }; } public static IEnumerable AddEntitySnapshots() { return from entitySnapshotAdder in AllEntitySnapshotAdders() - select new object[] { entitySnapshotAdder }; + select new object[] { entitySnapshotAdder }; } public static IEnumerable AddProjectionSnapshots() { return from projectionSnapshotAdder in AllProjectionSnapshotAdders() - select new object[] { projectionSnapshotAdder }; + select new object[] { projectionSnapshotAdder }; } - public static IEnumerable AddTransactionsAndEntitySnapshots() + public static IEnumerable AddSourcesAndEntitySnapshots() { - return from transactionAdder in AllTransactionAdders - from entitySnapshotAdder in AllEntitySnapshotAdders() - select new object[] { transactionAdder, entitySnapshotAdder }; + return from sourceAdder in AllSourceAdders + from entitySnapshotAdder in AllEntitySnapshotAdders() + select new object[] { sourceAdder, entitySnapshotAdder }; } - public static IEnumerable AddTransactionsEntitySnapshotsAndProjectionSnapshots() + public static IEnumerable AddSourcesEntitySnapshotsAndProjectionSnapshots() { - return from transactionAdder in AllTransactionAdders - from entitySnapshotAdder in AllEntitySnapshotAdders() - from projectionSnapshotAdder in AllProjectionSnapshotAdders() - select new object[] { transactionAdder, entitySnapshotAdder, projectionSnapshotAdder }; + return from sourceAdder in AllSourceAdders + from entitySnapshotAdder in AllEntitySnapshotAdders() + from projectionSnapshotAdder in AllProjectionSnapshotAdders() + select new object[] { sourceAdder, entitySnapshotAdder, projectionSnapshotAdder }; } protected IServiceScope CreateServiceScope(Action? configureServices = null) @@ -382,17 +301,11 @@ protected IServiceScope CreateServiceScope(Action? configure loggingBuilder.AddSimpleConsole(options => { options.IncludeScopes = true; }); }); - serviceCollection.Configure(x => - { - x.MinLevel = LogLevel.Debug; - }); + serviceCollection.Configure(x => { x.MinLevel = LogLevel.Debug; }); startup.AddServices(serviceCollection); - if (_databaseContainerFixture != null) - { - serviceCollection.AddSingleton(_databaseContainerFixture); - } + if (_databaseContainerFixture != null) serviceCollection.AddSingleton(_databaseContainerFixture); configureServices?.Invoke(serviceCollection); @@ -452,6 +365,8 @@ protected static (ILoggerFactory Logger, Action LoggerVerifier) GetMocked loggerFactoryMock .Setup(factory => factory.AddProvider(It.IsAny())); + return (loggerFactoryMock.Object, Verifier); + void Verifier(Times times) { loggerMock @@ -468,40 +383,39 @@ void Verifier(Times times) times ); } - - return (loggerFactoryMock.Object, Verifier); } - protected static ITransactionRepositoryFactory GetMockedTransactionRepositoryFactory( - object[]? commands = null) + protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( + object[]? deltas = null) { - commands ??= Array.Empty(); + deltas ??= Array.Empty(); - var transactionRepositoryMock = new Mock(MockBehavior.Strict); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); - transactionRepositoryMock - .Setup(repository => repository.PutTransaction(It.IsAny(), It.IsAny())) + sourceRepositoryMock + .Setup(repository => + repository.Commit(It.IsAny(), It.IsAny())) .ReturnsAsync(true); - transactionRepositoryMock - .Setup(repository => repository.EnumerateCommands(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerablePolyfill.FromResult(commands)); + sourceRepositoryMock + .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(deltas)); - transactionRepositoryMock + sourceRepositoryMock .Setup(repository => repository.DisposeAsync()) .Returns(ValueTask.CompletedTask); - var transactionRepositoryFactoryMock = - new Mock(MockBehavior.Strict); + var sourceRepositoryFactoryMock = + new Mock(MockBehavior.Strict); - transactionRepositoryFactoryMock + sourceRepositoryFactoryMock .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) - .ReturnsAsync(transactionRepositoryMock.Object); + .ReturnsAsync(sourceRepositoryMock.Object); - transactionRepositoryFactoryMock + sourceRepositoryFactoryMock .Setup(factory => factory.Dispose()); - return transactionRepositoryFactoryMock.Object; + return sourceRepositoryFactoryMock.Object; } protected static ISnapshotRepositoryFactory GetMockedSnapshotRepositoryFactory @@ -545,7 +459,8 @@ public void Dispose() } } - public record TransactionsAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, AddDependenciesDelegate AddDependencies) + public record SourcesAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, + AddDependenciesDelegate AddDependencies) { public override string ToString() { diff --git a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs b/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs deleted file mode 100644 index bb08244e..00000000 --- a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs +++ /dev/null @@ -1,1743 +0,0 @@ -using System.Collections.Immutable; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Builders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Agents; -using EntityDb.Common.Entities; -using EntityDb.Common.Exceptions; -using EntityDb.Common.Extensions; -using EntityDb.Common.Leases; -using EntityDb.Common.Queries; -using EntityDb.Common.Queries.Modified; -using EntityDb.Common.Tags; -using EntityDb.Common.Tests.Implementations.Commands; -using EntityDb.Common.Tests.Implementations.Leases; -using EntityDb.Common.Tests.Implementations.Queries; -using EntityDb.Common.Tests.Implementations.Seeders; -using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.Common.Tests.Implementations.Tags; -using EntityDb.Common.Transactions; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Moq; -using Shouldly; -using Xunit; - -namespace EntityDb.Common.Tests.Transactions; - -[Collection(nameof(DatabaseContainerCollection))] -public sealed class TransactionTests : TestsBase -{ - public TransactionTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) - { - } - - private static async Task InsertTransactions - ( - IServiceScope serviceScope, - List transactions - ) - { - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - foreach (var transaction in transactions) - { - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - transactionInserted.ShouldBeTrue(); - } - } - - private static ModifiedQueryOptions NewModifiedQueryOptions(bool invertFilter, bool reverseSort, int? replaceSkip, - int? replaceTake) - { - return new ModifiedQueryOptions - { - InvertFilter = invertFilter, - ReverseSort = reverseSort, - ReplaceSkip = replaceSkip, - ReplaceTake = replaceTake - }; - } - - private static async Task TestGet - ( - IServiceScope serviceScope, - Func getExpectedResults, - Func> getActualResults, - bool secondaryPreferred - ) - { - // ARRANGE - - var bufferModifier = NewModifiedQueryOptions(false, false, null, null); - var negateModifier = NewModifiedQueryOptions(true, false, null, null); - var reverseBufferModifier = NewModifiedQueryOptions(false, true, null, null); - var reverseNegateModifier = NewModifiedQueryOptions(true, true, null, null); - var bufferSubsetModifier = NewModifiedQueryOptions(false, false, 1, 1); - - var expectedTrueResults = getExpectedResults.Invoke(false); - var expectedFalseResults = getExpectedResults.Invoke(true); - var reversedExpectedTrueResults = expectedTrueResults.Reverse().ToArray(); - var reversedExpectedFalseResults = expectedFalseResults.Reverse().ToArray(); - var expectedSkipTakeResults = expectedTrueResults.Skip(1).Take(1).ToArray(); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(secondaryPreferred - ? TestSessionOptions.ReadOnlySecondaryPreferred - : TestSessionOptions.ReadOnly); - - // ACT - - var actualTrueResults = - await getActualResults.Invoke(transactionRepository, bufferModifier).ToArrayAsync(); - var actualFalseResults = - await getActualResults.Invoke(transactionRepository, negateModifier).ToArrayAsync(); - var reversedActualTrueResults = - await getActualResults.Invoke(transactionRepository, reverseBufferModifier).ToArrayAsync(); - var reversedActualFalseResults = - await getActualResults.Invoke(transactionRepository, reverseNegateModifier).ToArrayAsync(); - var actualSkipTakeResults = - await getActualResults.Invoke(transactionRepository, bufferSubsetModifier).ToArrayAsync(); - - // ASSERT - - actualTrueResults.ShouldBeEquivalentTo(expectedTrueResults); - actualFalseResults.ShouldBeEquivalentTo(expectedFalseResults); - reversedActualTrueResults.ShouldBeEquivalentTo(reversedExpectedTrueResults); - reversedActualFalseResults.ShouldBeEquivalentTo(reversedExpectedFalseResults); - actualSkipTakeResults.ShouldBeEquivalentTo(expectedSkipTakeResults); - } - - private static async Task TestGetTransactionIds - ( - IServiceScope serviceScope, - IAgentSignatureQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseTransactionIds - : expectedObjects.TrueTransactionIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateTransactionIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetTransactionIds - ( - IServiceScope serviceScope, - ICommandQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseTransactionIds - : expectedObjects.TrueTransactionIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateTransactionIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetTransactionIds - ( - IServiceScope serviceScope, - ILeaseQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseTransactionIds - : expectedObjects.TrueTransactionIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateTransactionIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetTransactionIds - ( - IServiceScope serviceScope, - ITagQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseTransactionIds - : expectedObjects.TrueTransactionIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateTransactionIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetEntityIds - ( - IServiceScope serviceScope, - IAgentSignatureQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateEntityIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetEntityIds - ( - IServiceScope serviceScope, - ICommandQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateEntityIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetEntityIds - ( - IServiceScope serviceScope, - ILeaseQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateEntityIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetEntityIds - ( - IServiceScope serviceScope, - ITagQuery query, - ExpectedObjects expectedObjects - ) - { - Id[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateEntityIds(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetAgentSignatures - ( - IServiceScope serviceScope, - IAgentSignatureQuery query, - ExpectedObjects expectedObjects - ) - { - object[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseAgentSignatures - : expectedObjects.TrueAgentSignatures) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateAgentSignatures(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetCommands - ( - IServiceScope serviceScope, - ICommandQuery query, - ExpectedObjects expectedObjects - ) - { - object[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseCommands - : expectedObjects.TrueCommands) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateCommands(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetLeases - ( - IServiceScope serviceScope, - ILeaseQuery query, - ExpectedObjects expectedObjects - ) - { - ILease[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseLeases - : expectedObjects.TrueLeases) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateLeases(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task TestGetTags - ( - IServiceScope serviceScope, - ITagQuery query, - ExpectedObjects expectedObjects - ) - { - ITag[] GetExpectedResults(bool invert) - { - return (invert - ? expectedObjects.FalseTags - : expectedObjects.TrueTags) - .ToArray(); - } - - IAsyncEnumerable GetActualResults(ITransactionRepository transactionRepository, - ModifiedQueryOptions modifiedQueryOptions) - { - return transactionRepository.EnumerateTags(query.Modify(modifiedQueryOptions)); - } - - await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); - await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - } - - private static async Task BuildTransaction - ( - IServiceScope serviceScope, - Id transactionId, - Id entityId, - IEnumerable counts, - TimeStamp? timeStampOverride = null, - object? agentSignatureOverride = null - ) - where TEntity : IEntity - { - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - foreach (var count in counts) - { - transactionBuilder.Append(new StoreNumber(count)); - } - - var transaction = (transactionBuilder.Build(transactionId) as Transaction).ShouldNotBeNull(); - - if (timeStampOverride.HasValue) - transaction = transaction with - { - TimeStamp = timeStampOverride.Value - }; - - if (agentSignatureOverride is not null) - transaction = transaction with - { - AgentSignature = agentSignatureOverride - }; - - return transaction; - } - - private static Id[] GetSortedIds(int numberOfIds) - { - return Enumerable - .Range(1, numberOfIds) - .Select(_ => Id.NewId()) - .OrderBy(id => id.Value) - .ToArray(); - } - - private async Task - Generic_GivenReadOnlyMode_WhenPuttingTransaction_ThenCannotWriteInReadOnlyModeExceptionIsLogged( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.RemoveAll(typeof(ILoggerFactory)); - - serviceCollection.AddSingleton(loggerFactory); - }); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var transaction = transactionBuilder - .Append(CommandSeeder.Create()) - .Build(default); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.ReadOnly); - - // ACT - - var inserted = await transactionRepository.PutTransaction(transaction); - - // ASSERT - - inserted.ShouldBeFalse(); - - loggerVerifier.Invoke(Times.Once()); - } - - private async Task Generic_GivenNonUniqueTransactionIds_WhenPuttingTransactions_ThenSecondPutReturnsFalse( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var transactionId = Id.NewId(); - - var firstTransactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var secondTransactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var firstTransaction = firstTransactionBuilder - .Append(CommandSeeder.Create()) - .Build(transactionId); - - var secondTransaction = secondTransactionBuilder - .Append(CommandSeeder.Create()) - .Build(transactionId); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - // ACT - - var firstTransactionInserted = await transactionRepository.PutTransaction(firstTransaction); - var secondTransactionInserted = await transactionRepository.PutTransaction(secondTransaction); - - // ASSERT - - firstTransactionInserted.ShouldBeTrue(); - secondTransactionInserted.ShouldBeFalse(); - } - - private async Task Generic_GivenNonUniqueVersionNumbers_WhenInsertingCommands_ThenReturnFalse( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - const int repeatCount = 2; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var transaction = (transactionBuilder - .Append(CommandSeeder.Create()) - .Build(default) - as Transaction)!; - - transaction = transaction with - { - Commands = Enumerable - .Repeat(transaction.Commands, repeatCount) - .SelectMany(commands => commands) - .ToImmutableArray() - }; - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - // ARRANGE ASSERTIONS - - repeatCount.ShouldBeGreaterThan(1); - - // ACT - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - // ASSERT - - transactionInserted.ShouldBeFalse(); - } - - private async Task - Generic_GivenVersionNumberZero_WhenInsertingCommands_ThenVersionZeroReservedExceptionIsLogged( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var versionNumber = new VersionNumber(0); - - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.RemoveAll(typeof(ILoggerFactory)); - - serviceCollection.AddSingleton(loggerFactory); - }); - - var transactionCommandMock = new Mock(MockBehavior.Strict); - - transactionCommandMock - .SetupGet(command => command.EntityId) - .Returns(default(Id)); - - transactionCommandMock - .SetupGet(command => command.EntityVersionNumber) - .Returns(versionNumber.Next()); - - transactionCommandMock - .SetupGet(command => command.EntityVersionNumber) - .Returns(versionNumber); - - var transaction = TransactionSeeder.Create(transactionCommandMock.Object, transactionCommandMock.Object); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - // ACT - - var transactionInserted = - await transactionRepository.PutTransaction(transaction); - - // ASSERT - - transactionInserted.ShouldBeFalse(); - - loggerVerifier.Invoke(Times.Once()); - } - - private async Task - Generic_GivenNonUniqueVersionNumbers_WhenInsertingCommands_ThenOptimisticConcurrencyExceptionIsLogged( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.RemoveAll(typeof(ILoggerFactory)); - - serviceCollection.AddSingleton(loggerFactory); - }); - - var entityId = Id.NewId(); - - var firstTransactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - var secondTransactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - var firstTransaction = firstTransactionBuilder - .Append(CommandSeeder.Create()) - .Build(Id.NewId()); - - var secondTransaction = secondTransactionBuilder - .Append(CommandSeeder.Create()) - .Build(Id.NewId()); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - // ACT - - var firstTransactionInserted = - await transactionRepository.PutTransaction(firstTransaction); - var secondTransactionInserted = - await transactionRepository.PutTransaction(secondTransaction); - - // ASSERT - - firstTransaction.Commands.Length.ShouldBe(1); - secondTransaction.Commands.Length.ShouldBe(1); - - firstTransaction.Commands.ShouldAllBe(command => command.EntityId == entityId); - secondTransaction.Commands.ShouldAllBe(command => command.EntityId == entityId); - - firstTransaction.Commands[0].EntityVersionNumber.ShouldBe(secondTransaction.Commands[0].EntityVersionNumber); - - firstTransactionInserted.ShouldBeTrue(); - secondTransactionInserted.ShouldBeFalse(); - - loggerVerifier.Invoke(Times.Once()); - } - - private async Task Generic_GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var tag = TagSeeder.Create(); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var transaction = transactionBuilder - .Append(new AddTag(tag)) - .Append(new AddTag(tag)) - .Build(default); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - // ACT - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - // ASSERT - - transactionInserted.ShouldBeTrue(); - } - - private async Task Generic_GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFalse( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var lease = LeaseSeeder.Create(); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var transaction = transactionBuilder - .Append(new AddLease(lease)) - .Append(new AddLease(lease)) - .Build(default); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - // ACT - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - // ASSERT - - transactionInserted.ShouldBeFalse(); - } - - private async Task - Generic_GivenCommandInserted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - const ulong expectedCount = 5; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var expectedTransactionId = Id.NewId(); - var expectedEntityId = Id.NewId(); - var expectedTransactionTimeStamp = transactionsAdder.FixTimeStamp(TimeStamp.UtcNow); - - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - var transaction = await BuildTransaction(serviceScope, expectedTransactionId, expectedEntityId, - new[] { expectedCount }, expectedTransactionTimeStamp, agentSignature); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - var agentSignatureQuery = new EntityIdQuery(expectedEntityId); - - // ARRANGE ASSERTIONS - - transactionInserted.ShouldBeTrue(); - - // ACT - - var annotatedAgentSignatures = await transactionRepository.EnumerateAnnotatedAgentSignatures(agentSignatureQuery).ToArrayAsync(); - - // ASSERT - - annotatedAgentSignatures.Length.ShouldBe(1); - - annotatedAgentSignatures[0].TransactionId.ShouldBe(expectedTransactionId); - annotatedAgentSignatures[0].TransactionTimeStamp.ShouldBe(expectedTransactionTimeStamp); - annotatedAgentSignatures[0].EntityIds.Length.ShouldBe(1); - annotatedAgentSignatures[0].EntityIds[0].ShouldBe(expectedEntityId); - annotatedAgentSignatures[0].Data.ShouldBeEquivalentTo(agentSignature); - } - - private async Task Generic_GivenCommandInserted_WhenGettingAnnotatedCommand_ThenReturnAnnotatedCommand( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - const ulong expectedCount = 5; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var expectedTransactionId = Id.NewId(); - var expectedEntityId = Id.NewId(); - var expectedTransactionTimeStamp = transactionsAdder.FixTimeStamp(TimeStamp.UtcNow); - - var transaction = await BuildTransaction(serviceScope, expectedTransactionId, expectedEntityId, - new[] { expectedCount }, expectedTransactionTimeStamp); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - var commandQuery = new GetEntityCommandsQuery(expectedEntityId, default); - - // ARRANGE ASSERTIONS - - transactionInserted.ShouldBeTrue(); - - // ACT - - var annotatedCommands = await transactionRepository.EnumerateAnnotatedCommands(commandQuery).ToArrayAsync(); - - // ASSERT - - annotatedCommands.Length.ShouldBe(1); - - annotatedCommands[0].TransactionId.ShouldBe(expectedTransactionId); - annotatedCommands[0].TransactionTimeStamp.ShouldBe(expectedTransactionTimeStamp); - annotatedCommands[0].EntityId.ShouldBe(expectedEntityId); - annotatedCommands[0].EntityVersionNumber.ShouldBe(new VersionNumber(1)); - - var actualCountCommand = annotatedCommands[0].Data.ShouldBeAssignableTo().ShouldNotBeNull(); - - actualCountCommand.Number.ShouldBe(expectedCount); - } - - private async Task Generic_GivenEntityInserted_WhenGettingEntity_ThenReturnEntity( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - var expectedEntity = TEntity.Construct(entityId).WithVersionNumber(new VersionNumber(1)); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - var entityRepository = EntityRepository.Create(serviceScope.ServiceProvider, transactionRepository); - - var transaction = await BuildTransaction(serviceScope, Id.NewId(), entityId, - new[] { 0UL }); - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - // ARRANGE ASSERTIONS - - transactionInserted.ShouldBeTrue(); - - // ACT - - var actualEntity = await entityRepository.GetSnapshot(entityId); - - // ASSERT - - actualEntity.ShouldBeEquivalentTo(expectedEntity); - } - - private async Task Generic_GivenEntityInsertedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - var tag = new Tag("Foo", "Bar"); - - var expectedInitialTags = new[] { tag }.ToImmutableArray(); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var initialTransaction = transactionBuilder - .Append(new AddTag(tag)) - .Build(Id.NewId()); - - var initialTransactionInserted = await transactionRepository.PutTransaction(initialTransaction); - - var tagQuery = new DeleteTagsQuery(entityId, expectedInitialTags); - - // ARRANGE ASSERTIONS - - initialTransactionInserted.ShouldBeTrue(); - - // ACT - - var actualInitialTags = await transactionRepository.EnumerateTags(tagQuery).ToArrayAsync(); - - var finalTransaction = transactionBuilder - .Append(new DeleteTag(tag)) - .Build(Id.NewId()); - - var finalTransactionInserted = await transactionRepository.PutTransaction(finalTransaction); - - var actualFinalTags = await transactionRepository.EnumerateTags(tagQuery).ToArrayAsync(); - - // ASSERT - - finalTransactionInserted.ShouldBeTrue(); - - expectedInitialTags.SequenceEqual(actualInitialTags).ShouldBeTrue(); - - actualFinalTags.ShouldBeEmpty(); - } - - private async Task Generic_GivenEntityInsertedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - var lease = new Lease("Foo", "Bar", "Baz"); - - var expectedInitialLeases = new[] { lease }.ToImmutableArray(); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var initialTransaction = transactionBuilder - .Append(new AddLease(lease)) - .Build(Id.NewId()); - - var initialTransactionInserted = await transactionRepository.PutTransaction(initialTransaction); - - var leaseQuery = new DeleteLeasesQuery(entityId, expectedInitialLeases); - - // ARRANGE ASSERTIONS - - initialTransactionInserted.ShouldBeTrue(); - - // ACT - - var actualInitialLeases = await transactionRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); - - var finalTransaction = transactionBuilder - .Append(new DeleteLease(lease)) - .Build(Id.NewId()); - - var finalTransactionInserted = await transactionRepository.PutTransaction(finalTransaction); - - var actualFinalLeases = await transactionRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); - - // ASSERT - - finalTransactionInserted.ShouldBeTrue(); - - actualInitialLeases.SequenceEqual(expectedInitialLeases).ShouldBeTrue(); - - actualFinalLeases.ShouldBeEmpty(); - } - - private async Task - Generic_GivenTransactionCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedCommand( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var expectedCommand = new StoreNumber(1); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var transaction = transactionBuilder - .Append(expectedCommand) - .Build(Id.NewId()); - - var versionOneCommandQuery = new EntityVersionNumberQuery(new VersionNumber(1), new VersionNumber(1)); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - // ACT - - var transactionInserted = await transactionRepository.PutTransaction(transaction); - - var newCommands = await transactionRepository.EnumerateCommands(versionOneCommandQuery).ToArrayAsync(); - - // ASSERT - - transactionInserted.ShouldBeTrue(); - - transaction.Commands.Length.ShouldBe(1); - - transaction.Commands[0].EntityVersionNumber.ShouldBe(new VersionNumber(1)); - - newCommands.Length.ShouldBe(1); - - newCommands[0].ShouldBeEquivalentTo(expectedCommand); - } - - private async Task - Generic_GivenTransactionAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedCommand< - TEntity>(TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var expectedCommand = new StoreNumber(2); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var firstTransaction = transactionBuilder - .Append(new StoreNumber(1)) - .Build(Id.NewId()); - - var secondTransaction = transactionBuilder - .Append(expectedCommand) - .Build(Id.NewId()); - - var versionTwoCommandQuery = new EntityVersionNumberQuery(new VersionNumber(2), new VersionNumber(2)); - - await using var transactionRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - var firstTransactionInserted = await transactionRepository.PutTransaction(firstTransaction); - - // ARRANGE ASSERTIONS - - firstTransactionInserted.ShouldBeTrue(); - - // ACT - - var secondTransactionInserted = await transactionRepository.PutTransaction(secondTransaction); - - var newCommands = await transactionRepository.EnumerateCommands(versionTwoCommandQuery).ToArrayAsync(); - - // ASSERT - - secondTransactionInserted.ShouldBeTrue(); - - secondTransaction.Commands.Length.ShouldBe(1); - - secondTransaction.Commands[0].EntityVersionNumber.ShouldBe(new VersionNumber(2)); - - newCommands.Length.ShouldBe(1); - - newCommands[0].ShouldBeEquivalentTo(expectedCommand); - } - - private async Task - Generic_GivenTransactionAlreadyInserted_WhenQueryingByTransactionTimeStamp_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - const ulong timeSpanInMinutes = 60UL; - const ulong gteInMinutes = 20UL; - const ulong lteInMinutes = 30UL; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var originTimeStamp = TimeStamp.UnixEpoch; - - var transactions = new List(); - var expectedObjects = new ExpectedObjects(); - - var transactionIds = GetSortedIds((int)timeSpanInMinutes); - var entityIds = GetSortedIds((int)timeSpanInMinutes); - - TimeStamp? gte = null; - TimeStamp? lte = null; - - for (var i = 1UL; i <= timeSpanInMinutes; i++) - { - var currentTransactionId = transactionIds[i - 1]; - var currentEntityId = entityIds[i - 1]; - - var currentTimeStamp = new TimeStamp(originTimeStamp.Value.AddMinutes(i)); - - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - var commands = new object[] { new StoreNumber(i) }; - - var leases = new[] { new CountLease(i) }; - - var tags = new[] { new CountTag(i) }; - - expectedObjects.Add(gteInMinutes <= i && i <= lteInMinutes, currentTransactionId, currentEntityId, - agentSignature, commands, leases, tags); - - switch (i) - { - case lteInMinutes: - lte = currentTimeStamp; - break; - - case gteInMinutes: - gte = currentTimeStamp; - break; - } - - var transaction = await BuildTransaction(serviceScope, currentTransactionId, currentEntityId, - new[] { i }, - currentTimeStamp, agentSignature); - - transactions.Add(transaction); - } - - gte.ShouldNotBeNull(); - lte.ShouldNotBeNull(); - - var query = new TransactionTimeStampQuery(gte.Value, lte.Value); - - await InsertTransactions(serviceScope, transactions); - await TestGetTransactionIds(serviceScope, query as IAgentSignatureQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ICommandQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IAgentSignatureQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ICommandQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetAgentSignatures(serviceScope, query, expectedObjects); - await TestGetCommands(serviceScope, query, expectedObjects); - await TestGetLeases(serviceScope, query, expectedObjects); - await TestGetTags(serviceScope, query, expectedObjects); - } - - private async Task - Generic_GivenTransactionAlreadyInserted_WhenQueryingByTransactionId_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - const ulong numberOfTransactionIds = 10UL; - const ulong whichTransactionId = 5UL; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var transactions = new List(); - var expectedObjects = new ExpectedObjects(); - - Id? transactionId = null; - - var transactionIds = GetSortedIds((int)numberOfTransactionIds); - var entityIds = GetSortedIds((int)numberOfTransactionIds); - - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - for (var i = 1UL; i <= numberOfTransactionIds; i++) - { - var currentTransactionId = transactionIds[i - 1]; - var currentEntityId = entityIds[i - 1]; - - var commands = new object[] { new StoreNumber(i) }; - - var leases = new[] { new CountLease(i) }; - - var tags = new[] { new CountTag(i) }; - - expectedObjects.Add(i == whichTransactionId, currentTransactionId, currentEntityId, agentSignature, - commands, - leases, tags); - - if (i == whichTransactionId) transactionId = currentTransactionId; - - var transaction = await BuildTransaction(serviceScope, currentTransactionId, currentEntityId, - new[] { i }, - agentSignatureOverride: agentSignature); - - transactions.Add(transaction); - } - - transactionId.ShouldNotBeNull(); - - var query = new TransactionIdQuery(transactionId.Value); - - await InsertTransactions(serviceScope, transactions); - await TestGetTransactionIds(serviceScope, query as IAgentSignatureQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ICommandQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IAgentSignatureQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ICommandQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetAgentSignatures(serviceScope, query, expectedObjects); - await TestGetCommands(serviceScope, query, expectedObjects); - await TestGetLeases(serviceScope, query, expectedObjects); - await TestGetTags(serviceScope, query, expectedObjects); - } - - private async Task - Generic_GivenTransactionAlreadyInserted_WhenQueryingByEntityId_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - const ulong numberOfEntityIds = 10UL; - const ulong whichEntityId = 5UL; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var transactions = new List(); - var expectedObjects = new ExpectedObjects(); - - Id? entityId = null; - - var transactionIds = GetSortedIds((int)numberOfEntityIds); - var entityIds = GetSortedIds((int)numberOfEntityIds); - - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - for (var i = 1UL; i <= numberOfEntityIds; i++) - { - var currentTransactionId = transactionIds[i - 1]; - var currentEntityId = entityIds[i - 1]; - - var commands = new object[] { new StoreNumber(i) }; - - var leases = new[] { new CountLease(i) }; - - var tags = new[] { new CountTag(i) }; - - expectedObjects.Add(i == whichEntityId, currentTransactionId, currentEntityId, agentSignature, commands, - leases, tags); - - if (i == whichEntityId) entityId = currentEntityId; - - var transaction = await BuildTransaction(serviceScope, currentTransactionId, currentEntityId, - new[] { i }, - agentSignatureOverride: agentSignature); - - transactions.Add(transaction); - } - - entityId.ShouldNotBeNull(); - - var query = new EntityIdQuery(entityId.Value); - - await InsertTransactions(serviceScope, transactions); - await TestGetTransactionIds(serviceScope, query as IAgentSignatureQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ICommandQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IAgentSignatureQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ICommandQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetAgentSignatures(serviceScope, query, expectedObjects); - await TestGetCommands(serviceScope, query, expectedObjects); - await TestGetLeases(serviceScope, query, expectedObjects); - await TestGetTags(serviceScope, query, expectedObjects); - } - - private async Task - Generic_GivenTransactionAlreadyInserted_WhenQueryingByEntityVersionNumber_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TEntity : IEntity - { - const ulong numberOfVersionNumbers = 20; - const ulong gte = 5UL; - const ulong lte = 15UL; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var counts = new List(); - var expectedObjects = new ExpectedObjects(); - - for (var i = 1UL; i <= numberOfVersionNumbers; i++) - { - var command = new StoreNumber(i); - - var leases = new[] { new CountLease(i) }; - - var tags = new[] { new CountTag(i) }; - - counts.Add(i); - - expectedObjects.Add(i is >= gte and <= lte, default, default, default!, new[] { command }, - leases, tags); - } - - var transaction = await BuildTransaction(serviceScope, Id.NewId(), Id.NewId(), counts.ToArray()); - - var transactions = new List { transaction }; - - var query = new EntityVersionNumberQuery(new VersionNumber(gte), new VersionNumber(lte)); - - await InsertTransactions(serviceScope, transactions); - await TestGetCommands(serviceScope, query, expectedObjects); - await TestGetLeases(serviceScope, query, expectedObjects); - await TestGetTags(serviceScope, query, expectedObjects); - } - - private async Task Generic_GivenTransactionAlreadyInserted_WhenQueryingByData_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - where TOptions : class - where TEntity : IEntity - { - const ulong countTo = 20UL; - const ulong gte = 5UL; - const ulong lte = 15UL; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var transactions = new List(); - var expectedObjects = new ExpectedObjects(); - - var transactionIds = GetSortedIds((int)countTo); - var entityIds = GetSortedIds((int)countTo); - - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - var commands = new object[] { new DoNothing() }; - - for (var i = 1UL; i <= countTo; i++) - { - var currentTransactionId = transactionIds[i - 1]; - var currentEntityId = entityIds[i - 1]; - - var leases = new[] { new CountLease(i) }; - - var tags = new[] { new CountTag(i) }; - - expectedObjects.Add(i is >= gte and <= lte, currentTransactionId, currentEntityId, agentSignature, commands, - leases, tags); - - var transaction = await BuildTransaction(serviceScope, currentTransactionId, currentEntityId, - new[] { i }, - agentSignatureOverride: agentSignature); - - transactions.Add(transaction); - } - - var options = serviceScope.ServiceProvider - .GetRequiredService>() - .Create("Count"); - - var query = new CountQuery(gte, lte, options); - - await InsertTransactions(serviceScope, transactions); - await TestGetTransactionIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetTransactionIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetLeases(serviceScope, query, expectedObjects); - await TestGetTags(serviceScope, query, expectedObjects); - } - - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenReadOnlyMode_WhenPuttingTransaction_ThenCannotWriteInReadOnlyModeExceptionIsLogged( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenNonUniqueTransactionIds_WhenPuttingTransactions_ThenSecondPutReturnsFalse( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenNonUniqueVersionNumbers_WhenInsertingCommands_ThenReturnFalse(TransactionsAdder transactionsAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenVersionNumberZero_WhenInsertingCommands_ThenVersionZeroReservedExceptionIsLogged( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenNonUniqueVersionNumbers_WhenInsertingCommands_ThenOptimisticConcurrencyExceptionIsLogged( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue(TransactionsAdder transactionsAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFalse(TransactionsAdder transactionsAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenCommandInserted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenCommandInserted_WhenGettingAnnotatedCommand_ThenReturnAnnotatedCommand( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenEntityInserted_WhenGettingEntity_ThenReturnEntity(TransactionsAdder transactionsAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenEntityInsertedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenEntityInsertedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedCommand( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedCommand( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionAlreadyInserted_WhenQueryingByTransactionTimeStamp_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionAlreadyInserted_WhenQueryingByTransactionId_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionAlreadyInserted_WhenQueryingByEntityId_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionAlreadyInserted_WhenQueryingByEntityVersionNumber_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntity))] - public Task GivenTransactionAlreadyInserted_WhenQueryingByData_ThenReturnExpectedObjects( - TransactionsAdder transactionsAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { transactionsAdder.QueryOptionsType, entityAdder.EntityType }, - new object?[] { transactionsAdder, entityAdder } - ); - } - - private class ExpectedObjects - { - public readonly List FalseAgentSignatures = new(); - public readonly List FalseCommands = new(); - public readonly List FalseEntityIds = new(); - public readonly List FalseLeases = new(); - public readonly List FalseTags = new(); - public readonly List FalseTransactionIds = new(); - public readonly List TrueAgentSignatures = new(); - - public readonly List TrueCommands = new(); - public readonly List TrueEntityIds = new(); - public readonly List TrueLeases = new(); - public readonly List TrueTags = new(); - public readonly List TrueTransactionIds = new(); - - public void Add - ( - bool condition, - Id transactionId, - Id entityId, - object agentSignature, - IEnumerable commands, - IEnumerable leases, - IEnumerable tags - ) - { - if (condition) - { - TrueTransactionIds.Add(transactionId); - TrueEntityIds.Add(entityId); - TrueAgentSignatures.Add(agentSignature); - TrueCommands.AddRange(commands); - TrueLeases.AddRange(leases); - TrueTags.AddRange(tags); - } - else - { - FalseTransactionIds.Add(transactionId); - FalseEntityIds.Add(entityId); - FalseAgentSignatures.Add(agentSignature); - FalseCommands.AddRange(commands); - FalseLeases.AddRange(leases); - FalseTags.AddRange(tags); - } - } - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Transactions/TryCatchTransactionRepositoryTests.cs b/test/EntityDb.Common.Tests/Transactions/TryCatchTransactionRepositoryTests.cs deleted file mode 100644 index 8b2a7810..00000000 --- a/test/EntityDb.Common.Tests/Transactions/TryCatchTransactionRepositoryTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Transactions; -using Microsoft.Extensions.Logging; -using Moq; -using Shouldly; -using Xunit; - -namespace EntityDb.Common.Tests.Transactions; - -public class TryCatchTransactionRepositoryTests : TestsBase -{ - public TryCatchTransactionRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - - [Fact] - public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged() - { - // ARRANGE - - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - - var transactionRepositoryMock = new Mock(MockBehavior.Strict); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateTransactionIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateTransactionIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateTransactionIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateTransactionIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateEntityIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateEntityIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateEntityIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateEntityIds(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateCommands(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateLeases(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.EnumerateTags(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => - repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => - repository.EnumerateAnnotatedCommands(It.IsAny(), It.IsAny())) - .Throws(new NotImplementedException()); - - transactionRepositoryMock - .Setup(repository => repository.PutTransaction(It.IsAny(), It.IsAny())) - .ThrowsAsync(new NotImplementedException()); - - var tryCatchTransactionRepository = new TryCatchTransactionRepository(transactionRepositoryMock.Object, - loggerFactory.CreateLogger()); - - // ACT - - var transactionIdsFromAgentSignatureQuery = - await tryCatchTransactionRepository.EnumerateTransactionIds(default(IAgentSignatureQuery)!).ToArrayAsync(); - var transactionIdsFromCommandQuery = - await tryCatchTransactionRepository.EnumerateTransactionIds(default(ICommandQuery)!).ToArrayAsync(); - var transactionIdsFromLeaseQuery = - await tryCatchTransactionRepository.EnumerateTransactionIds(default(ILeaseQuery)!).ToArrayAsync(); - var transactionIdsFromTagQuery = - await tryCatchTransactionRepository.EnumerateTransactionIds(default(ITagQuery)!).ToArrayAsync(); - var entityIdsFromAgentSignatureQuery = - await tryCatchTransactionRepository.EnumerateEntityIds(default(IAgentSignatureQuery)!).ToArrayAsync(); - var entityIdsFromCommandQuery = - await tryCatchTransactionRepository.EnumerateEntityIds(default(ICommandQuery)!).ToArrayAsync(); - var entityIdsFromLeaseQuery = - await tryCatchTransactionRepository.EnumerateEntityIds(default(ILeaseQuery)!).ToArrayAsync(); - var entityIdsFromTagQuery = - await tryCatchTransactionRepository.EnumerateEntityIds(default(ITagQuery)!).ToArrayAsync(); - var agentSignatures = - await tryCatchTransactionRepository.EnumerateAgentSignatures(default!).ToArrayAsync(); - var commands = - await tryCatchTransactionRepository.EnumerateCommands(default!).ToArrayAsync(); - var leases = - await tryCatchTransactionRepository.EnumerateLeases(default!).ToArrayAsync(); - var tags = - await tryCatchTransactionRepository.EnumerateTags(default!).ToArrayAsync(); - var annotatedCommands = - await tryCatchTransactionRepository.EnumerateAnnotatedCommands(default!).ToArrayAsync(); - var inserted = - await tryCatchTransactionRepository.PutTransaction(default!); - - // ASSERT - - transactionIdsFromAgentSignatureQuery.ShouldBeEmpty(); - transactionIdsFromCommandQuery.ShouldBeEmpty(); - transactionIdsFromLeaseQuery.ShouldBeEmpty(); - transactionIdsFromTagQuery.ShouldBeEmpty(); - entityIdsFromAgentSignatureQuery.ShouldBeEmpty(); - entityIdsFromCommandQuery.ShouldBeEmpty(); - entityIdsFromLeaseQuery.ShouldBeEmpty(); - entityIdsFromTagQuery.ShouldBeEmpty(); - agentSignatures.ShouldBeEmpty(); - commands.ShouldBeEmpty(); - leases.ShouldBeEmpty(); - tags.ShouldBeEmpty(); - annotatedCommands.ShouldBeEmpty(); - inserted.ShouldBeFalse(); - loggerVerifier.Invoke(Times.Exactly(14)); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs index 9e3d8676..fdbec3b2 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs @@ -103,7 +103,7 @@ public void GivenNoTypeInformation_WhenLoadingType_ThenReturnNull() var envelopeHeaders = new EnvelopeHeaders(new Dictionary { - [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform + [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, }); // ACT @@ -134,7 +134,7 @@ public void GivenGarbageTypeInformation_WhenLoadingType_ThenThrow() [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, [EnvelopeHelper.AssemblyFullName] = "Garbage", [EnvelopeHelper.TypeFullName] = "Garbage", - [EnvelopeHelper.MemberInfoName] = "Garbage" + [EnvelopeHelper.MemberInfoName] = "Garbage", }); // ASSERT diff --git a/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs index d06c6b4a..36fdb712 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs @@ -17,7 +17,7 @@ public void GivenMemberInfoNameTypeResolverKnowsExpectedType_WhenResolvingType_T var envelopeHeaders = new EnvelopeHeaders(new Dictionary { [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, - [EnvelopeHelper.MemberInfoName] = expectedType.Name + [EnvelopeHelper.MemberInfoName] = expectedType.Name, }); var typeResolver = new MemberInfoNamePartialTypeResolver(new[] { expectedType }); @@ -61,7 +61,7 @@ public void GivenEmptyMemberInfoNameTypeResolver_WhenResolvingType_ThenReturnNul var envelopeHeaders = new EnvelopeHeaders(new Dictionary { [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, - [EnvelopeHelper.MemberInfoName] = "" + [EnvelopeHelper.MemberInfoName] = "", }); // ACT diff --git a/test/EntityDb.Common.Tests/packages.lock.json b/test/EntityDb.Common.Tests/packages.lock.json index 4d67a9bb..75722588 100644 --- a/test/EntityDb.Common.Tests/packages.lock.json +++ b/test/EntityDb.Common.Tests/packages.lock.json @@ -33,18 +33,6 @@ "Castle.Core": "5.1.0" } }, - "Npgsql.EntityFrameworkCore.PostgreSQL": { - "type": "Direct", - "requested": "[7.0.4, )", - "resolved": "7.0.4", - "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", - "Npgsql": "7.0.4" - } - }, "Shouldly": { "type": "Direct", "requested": "[4.2.1, )", @@ -74,16 +62,6 @@ "Testcontainers": "3.0.0" } }, - "Testcontainers.PostgreSql": { - "type": "Direct", - "requested": "[3.0.0, )", - "resolved": "3.0.0", - "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", - "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" - } - }, "Testcontainers.Redis": { "type": "Direct", "requested": "[3.0.0, )", @@ -218,57 +196,6 @@ "resolved": "17.7.1", "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, - "Microsoft.EntityFrameworkCore": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", - "Microsoft.Extensions.Caching.Memory": "7.0.0", - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.Logging": "7.0.0" - } - }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" - }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.10", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -280,10 +207,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -349,16 +276,17 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -423,19 +351,20 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0" + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + "resolved": "6.0.3", + "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -503,11 +432,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -524,8 +453,11 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -671,14 +603,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" - } - }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "6.5.0", @@ -909,14 +833,10 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Diagnostics.EventLog": { @@ -1694,14 +1614,6 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.entityframework": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.inmemory": { "type": "Project", "dependencies": { @@ -1724,19 +1636,10 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { - "type": "Project", - "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.4, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.provisioner": { "type": "Project", "dependencies": { "EntityDb.MongoDb": "[1.0.0, )", - "EntityDb.Npgsql": "[1.0.0, )", "System.CommandLine": "[2.0.0-beta4.22272.1, )", "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", "System.Linq.Async": "[6.0.1, )" @@ -1750,14 +1653,6 @@ "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } - }, - "entitydb.sqldb": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } } } } diff --git a/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj b/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj index 7821afba..8577fd3b 100644 --- a/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj +++ b/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj @@ -1,8 +1,8 @@  - - + + diff --git a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs index 60be64cb..cb9a248a 100644 --- a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs +++ b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs @@ -1,6 +1,6 @@ using EntityDb.Common.Envelopes; using EntityDb.Common.Tests.Envelopes; -using EntityDb.MongoDb.Envelopes; +using EntityDb.MongoDb.Documents.Envelopes; using EntityDb.MongoDb.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -46,7 +46,8 @@ public void GivenTypeDiscriminatorShouldBeRemovedOption_ThereBsonDocumentMatches var bsonDocumentEnvelope = envelopeService.Serialize(value); var actualContainsTypeDiscriminatorProperty = - bsonDocumentEnvelope.GetElement("Value").Value.AsBsonDocument.Contains(MongoDbEnvelopeService.TypeDiscriminatorPropertyName); + bsonDocumentEnvelope.GetElement("Value").Value.AsBsonDocument + .Contains(MongoDbEnvelopeService.TypeDiscriminatorPropertyName); // ASSERT diff --git a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs index f96c88f8..3ec0ffec 100644 --- a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs +++ b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs @@ -1,6 +1,6 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests; -using EntityDb.MongoDb.Transactions.Sessions; +using EntityDb.MongoDb.Sources.Sessions; using Shouldly; using Xunit; @@ -17,9 +17,9 @@ public async Task WhenExecutingWriteMethods_ThenThrow() { // ARRANGE - var mongoSession = new MongoSession(default!, default!, default!, new MongoDbTransactionSessionOptions + var mongoSession = new MongoSession(default!, default!, default!, new MongoDbSourceSessionOptions { - ReadOnly = true + ReadOnly = true, }); // ASSERT diff --git a/test/EntityDb.MongoDb.Tests/packages.lock.json b/test/EntityDb.MongoDb.Tests/packages.lock.json index 43ba1fed..81c3796d 100644 --- a/test/EntityDb.MongoDb.Tests/packages.lock.json +++ b/test/EntityDb.MongoDb.Tests/packages.lock.json @@ -176,57 +176,6 @@ "resolved": "17.7.1", "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, - "Microsoft.EntityFrameworkCore": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", - "Microsoft.Extensions.Caching.Memory": "7.0.0", - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.Logging": "7.0.0" - } - }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" - }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.10", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -238,10 +187,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -307,16 +256,17 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -381,19 +331,20 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0" + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + "resolved": "6.0.3", + "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -461,11 +412,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -482,8 +433,11 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -629,25 +583,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" - } - }, - "Npgsql.EntityFrameworkCore.PostgreSQL": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", - "Npgsql": "7.0.4" - } - }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "6.5.0", @@ -878,14 +813,10 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Diagnostics.EventLog": { @@ -1609,15 +1540,6 @@ "Testcontainers": "3.0.0" } }, - "Testcontainers.PostgreSql": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", - "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" - } - }, "Testcontainers.Redis": { "type": "Transitive", "resolved": "3.0.0", @@ -1695,38 +1617,19 @@ "dependencies": { "Bogus": "[34.0.2, )", "EntityDb.Common": "[1.0.0, )", - "EntityDb.EntityFramework": "[1.0.0, )", - "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", "Microsoft.NET.Test.Sdk": "[17.7.1, )", "Moq": "[4.18.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.4, )", "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", "Testcontainers.MongoDb": "[3.0.0, )", - "Testcontainers.PostgreSql": "[3.0.0, )", "Testcontainers.Redis": "[3.0.0, )", "Xunit.DependencyInjection": "[8.8.2, )", "Xunit.DependencyInjection.Logging": "[8.1.0, )", "xunit": "[2.5.0, )" } }, - "entitydb.entityframework": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.inmemory": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.json": { "type": "Project", "dependencies": { @@ -1742,19 +1645,10 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { - "type": "Project", - "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.4, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.provisioner": { "type": "Project", "dependencies": { "EntityDb.MongoDb": "[1.0.0, )", - "EntityDb.Npgsql": "[1.0.0, )", "System.CommandLine": "[2.0.0-beta4.22272.1, )", "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", "System.Linq.Async": "[6.0.1, )" @@ -1768,14 +1662,6 @@ "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } - }, - "entitydb.sqldb": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } } } } diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs index 8a27a3a6..6e47f48b 100644 --- a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs +++ b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs @@ -46,24 +46,24 @@ protected override IEnumerable GetAgentAccessorOptions { Headers = new Dictionary { - ["Content-Type"] = new[] { "application/json" } + ["Content-Type"] = new[] { "application/json" }, }, - HasIpAddress = true + HasIpAddress = true, }, new HttpContextSeederOptions { Headers = new Dictionary { - ["Content-Type"] = new[] { "application/json" } + ["Content-Type"] = new[] { "application/json" }, }, - HasIpAddress = false - } + HasIpAddress = false, + }, }; } protected override Dictionary? GetApplicationInfo(object agentSignature) { - return agentSignature is not HttpContextAgentSignature.Snapshot httpContextAgentSignature + return agentSignature is not HttpContextAgentSignature httpContextAgentSignature ? null : httpContextAgentSignature.ApplicationInfo; } diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs index 7c12431e..77e6b6d0 100644 --- a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs +++ b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs @@ -17,27 +17,27 @@ public void GivenNoRedactedHeaders_WhenHttpContextHasHeader_ThenAgentSignatureHa var httpContextAgentOptions = new HttpContextAgentSignatureOptions { - RedactedHeaders = Array.Empty() + RedactedHeaders = Array.Empty(), }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions { Headers = new Dictionary { - [headerName] = new[] { headerValue } - } + [headerName] = new[] { headerValue }, + }, }); // ACT - var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); + var signature = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); // ASSERT - request.Headers.Length.ShouldBe(1); - request.Headers[0].Name.ShouldBe(headerName); - request.Headers[0].Values.Length.ShouldBe(1); - request.Headers[0].Values[0].ShouldBe(headerValue); + signature.Request.Headers.Length.ShouldBe(1); + signature.Request.Headers[0].Name.ShouldBe(headerName); + signature.Request.Headers[0].Values.Length.ShouldBe(1); + signature.Request.Headers[0].Values[0].ShouldBe(headerValue); } [Fact] @@ -52,27 +52,27 @@ public void GivenRedactedHeader_WhenHttpContextContainsOnlyThatHeader_ThenAgentS var httpContextAgentOptions = new HttpContextAgentSignatureOptions { RedactedHeaders = new[] { headerName }, - RedactedValue = redactedValue + RedactedValue = redactedValue, }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions { Headers = new Dictionary { - [headerName] = new[] { headerValue } - } + [headerName] = new[] { headerValue }, + }, }); // ACT - var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); + var signature = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); // ASSERT - request.Headers.Length.ShouldBe(1); - request.Headers[0].Name.ShouldBe(headerName); - request.Headers[0].Values.Length.ShouldBe(1); - request.Headers[0].Values[0].ShouldBe(redactedValue); + signature.Request.Headers.Length.ShouldBe(1); + signature.Request.Headers[0].Name.ShouldBe(headerName); + signature.Request.Headers[0].Values.Length.ShouldBe(1); + signature.Request.Headers[0].Values[0].ShouldBe(redactedValue); } [Fact] @@ -86,27 +86,27 @@ public void var httpContextAgentOptions = new HttpContextAgentSignatureOptions { - RedactedQueryStringParams = Array.Empty() + RedactedQueryStringParams = Array.Empty(), }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions { QueryStringParams = new Dictionary { - [queryStringParamName] = new[] { queryStringParamValue } - } + [queryStringParamName] = new[] { queryStringParamValue }, + }, }); // ACT - var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); + var signature = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); // ASSERT - request.QueryStringParams.Length.ShouldBe(1); - request.QueryStringParams[0].Name.ShouldBe(queryStringParamName); - request.QueryStringParams[0].Values.Length.ShouldBe(1); - request.QueryStringParams[0].Values[0].ShouldBe(queryStringParamValue); + signature.Request.QueryStringParams.Length.ShouldBe(1); + signature.Request.QueryStringParams[0].Name.ShouldBe(queryStringParamName); + signature.Request.QueryStringParams[0].Values.Length.ShouldBe(1); + signature.Request.QueryStringParams[0].Values[0].ShouldBe(queryStringParamValue); } [Fact] @@ -122,26 +122,26 @@ public void var httpContextAgentOptions = new HttpContextAgentSignatureOptions { RedactedQueryStringParams = new[] { queryStringParamName }, - RedactedValue = redactedValue + RedactedValue = redactedValue, }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions { QueryStringParams = new Dictionary { - [queryStringParamName] = new[] { queryStringParamValue } - } + [queryStringParamName] = new[] { queryStringParamValue }, + }, }); // ACT - var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); + var signature = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!); // ASSERT - request.QueryStringParams.Length.ShouldBe(1); - request.QueryStringParams[0].Name.ShouldBe(queryStringParamName); - request.QueryStringParams[0].Values.Length.ShouldBe(1); - request.QueryStringParams[0].Values[0].ShouldBe(redactedValue); + signature.Request.QueryStringParams.Length.ShouldBe(1); + signature.Request.QueryStringParams[0].Name.ShouldBe(queryStringParamName); + signature.Request.QueryStringParams[0].Values.Length.ShouldBe(1); + signature.Request.QueryStringParams[0].Values[0].ShouldBe(redactedValue); } } \ No newline at end of file diff --git a/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj b/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj index 556f9cf3..6f7592b4 100644 --- a/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj +++ b/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj @@ -1,8 +1,8 @@  - - + + diff --git a/test/EntityDb.Mvc.Tests/packages.lock.json b/test/EntityDb.Mvc.Tests/packages.lock.json index 9f4090b5..810baf2c 100644 --- a/test/EntityDb.Mvc.Tests/packages.lock.json +++ b/test/EntityDb.Mvc.Tests/packages.lock.json @@ -176,57 +176,6 @@ "resolved": "17.7.1", "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, - "Microsoft.EntityFrameworkCore": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", - "Microsoft.Extensions.Caching.Memory": "7.0.0", - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.Logging": "7.0.0" - } - }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" - }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.10", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -238,10 +187,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -307,16 +256,17 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -381,19 +331,20 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0" + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + "resolved": "6.0.3", + "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -461,11 +412,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -482,8 +433,11 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -629,25 +583,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" - } - }, - "Npgsql.EntityFrameworkCore.PostgreSQL": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", - "Npgsql": "7.0.4" - } - }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "6.5.0", @@ -878,14 +813,10 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Diagnostics.EventLog": { @@ -1609,15 +1540,6 @@ "Testcontainers": "3.0.0" } }, - "Testcontainers.PostgreSql": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", - "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" - } - }, "Testcontainers.Redis": { "type": "Transitive", "resolved": "3.0.0", @@ -1695,38 +1617,19 @@ "dependencies": { "Bogus": "[34.0.2, )", "EntityDb.Common": "[1.0.0, )", - "EntityDb.EntityFramework": "[1.0.0, )", - "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", "Microsoft.NET.Test.Sdk": "[17.7.1, )", "Moq": "[4.18.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.4, )", "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", "Testcontainers.MongoDb": "[3.0.0, )", - "Testcontainers.PostgreSql": "[3.0.0, )", "Testcontainers.Redis": "[3.0.0, )", "Xunit.DependencyInjection": "[8.8.2, )", "Xunit.DependencyInjection.Logging": "[8.1.0, )", "xunit": "[2.5.0, )" } }, - "entitydb.entityframework": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.inmemory": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.json": { "type": "Project", "dependencies": { @@ -1749,19 +1652,10 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { - "type": "Project", - "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.4, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.provisioner": { "type": "Project", "dependencies": { "EntityDb.MongoDb": "[1.0.0, )", - "EntityDb.Npgsql": "[1.0.0, )", "System.CommandLine": "[2.0.0-beta4.22272.1, )", "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", "System.Linq.Async": "[6.0.1, )" @@ -1775,14 +1669,6 @@ "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } - }, - "entitydb.sqldb": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } } } } diff --git a/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj b/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj index 4bb94292..2d9d164d 100644 --- a/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj +++ b/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj @@ -1,8 +1,8 @@  - - + + diff --git a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs index 9c0a0bf8..ddaaad2a 100644 --- a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs +++ b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs @@ -1,6 +1,6 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests; -using EntityDb.Redis.Sessions; +using EntityDb.Redis.Snapshots.Sessions; using Shouldly; using Xunit; @@ -19,7 +19,7 @@ public async Task WhenExecutingWriteMethods_ThenThrow() var readOnlyRedisSession = new RedisSession(default!, default!, new RedisSnapshotSessionOptions { - ReadOnly = true + ReadOnly = true, }); // ASSERT diff --git a/test/EntityDb.Redis.Tests/packages.lock.json b/test/EntityDb.Redis.Tests/packages.lock.json index 43ba1fed..81c3796d 100644 --- a/test/EntityDb.Redis.Tests/packages.lock.json +++ b/test/EntityDb.Redis.Tests/packages.lock.json @@ -176,57 +176,6 @@ "resolved": "17.7.1", "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" }, - "Microsoft.EntityFrameworkCore": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", - "Microsoft.Extensions.Caching.Memory": "7.0.0", - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.Logging": "7.0.0" - } - }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" - }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.10", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "6.0.0", @@ -238,10 +187,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -307,16 +256,17 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", @@ -381,19 +331,20 @@ }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0" + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" + "resolved": "6.0.3", + "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -461,11 +412,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -482,8 +433,11 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -629,25 +583,6 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" - } - }, - "Npgsql.EntityFrameworkCore.PostgreSQL": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "ZYMtyG6pmLtUsFAx0/XaIlVkJM+1gArWEKD55cLLxiVlGScAphjiGj+G7Gk16yg5lhhdWx+bgXWpIUISXuS33g==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Abstractions": "[7.0.5, 8.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, 8.0.0)", - "Npgsql": "7.0.4" - } - }, "NuGet.Frameworks": { "type": "Transitive", "resolved": "6.5.0", @@ -878,14 +813,10 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Diagnostics.EventLog": { @@ -1609,15 +1540,6 @@ "Testcontainers": "3.0.0" } }, - "Testcontainers.PostgreSql": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "gr+jJF6X8r5cXcVUm8BQwJcrePd3jGKoBFlGCm7z5gOidgusXpkGoAnQCgdkexfCGnbozHN9BbGupIjuovym7A==", - "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" - } - }, "Testcontainers.Redis": { "type": "Transitive", "resolved": "3.0.0", @@ -1695,38 +1617,19 @@ "dependencies": { "Bogus": "[34.0.2, )", "EntityDb.Common": "[1.0.0, )", - "EntityDb.EntityFramework": "[1.0.0, )", - "EntityDb.InMemory": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", "Microsoft.NET.Test.Sdk": "[17.7.1, )", "Moq": "[4.18.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[7.0.4, )", "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", "Testcontainers.MongoDb": "[3.0.0, )", - "Testcontainers.PostgreSql": "[3.0.0, )", "Testcontainers.Redis": "[3.0.0, )", "Xunit.DependencyInjection": "[8.8.2, )", "Xunit.DependencyInjection.Logging": "[8.1.0, )", "xunit": "[2.5.0, )" } }, - "entitydb.entityframework": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.10, )", - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.inmemory": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.json": { "type": "Project", "dependencies": { @@ -1742,19 +1645,10 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { - "type": "Project", - "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.4, )", - "System.Linq.Async": "[6.0.1, )" - } - }, "entitydb.provisioner": { "type": "Project", "dependencies": { "EntityDb.MongoDb": "[1.0.0, )", - "EntityDb.Npgsql": "[1.0.0, )", "System.CommandLine": "[2.0.0-beta4.22272.1, )", "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", "System.Linq.Async": "[6.0.1, )" @@ -1768,14 +1662,6 @@ "StackExchange.Redis": "[2.6.122, )", "System.Linq.Async": "[6.0.1, )" } - }, - "entitydb.sqldb": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } } } } From 7eef2e03379626a8b60ff9b8a97f0d3a43fcd2cb Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 62/93] feat: message ids for idempotency when using Version.Zero --- .../Projections/IProjection.cs | 2 +- .../Annotations/IAnnotatedSourceData.cs | 5 ++++ .../Annotations/IAnnotatedSourceGroupData.cs | 7 +++++- src/EntityDb.Abstractions/Sources/Message.cs | 5 ++++ .../Sources/Queries/IMessageQuery.cs | 2 +- .../SortBuilders/IMessageSortBuilder.cs | 2 +- .../Entities/EntitySourceBuilder.cs | 9 ++++---- src/EntityDb.Common/EntityDb.Common.csproj | 4 +--- .../Extensions/SourceRepositoryExtensions.cs | 10 ++++---- .../Annotations/AnnotatedSourceData.cs | 3 +++ .../Annotations/AnnotatedSourceGroupData.cs | 3 +++ .../Sources/Documents/DocumentsExtensions.cs | 2 ++ .../Sources/Documents/IMessageDocument.cs | 1 + .../Documents/IMessageGroupDocument.cs | 1 + .../Documents/AgentSignatureDocument.cs | 5 ++++ .../Documents/DeltaDocument.cs | 1 + .../Documents/LeaseDocument.cs | 1 + .../Documents/MessageDocumentBase.cs | 2 ++ .../Documents/MessageGroupDocumentBase.cs | 2 ++ src/EntityDb.MongoDb/Documents/TagDocument.cs | 1 + .../Extensions/MongoClientExtensions.cs | 23 +++++++++++++++++-- src/EntityDb.Mvc/EntityDb.Mvc.csproj | 1 - .../EntityDb.Provisioner.csproj | 2 -- src/EntityDb.Redis/EntityDb.Redis.csproj | 1 - .../Implementations/Seeders/MessageSeeder.cs | 1 + .../Implementations/Seeders/SourceSeeder.cs | 7 +++--- .../Sources/SourceTests.cs | 1 + 27 files changed, 80 insertions(+), 24 deletions(-) diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index 2f56420c..9146872a 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -24,7 +24,7 @@ public interface IProjection : ISnapshot /// A pointer to the desired projection state /// A cancellation token /// - /// A that is used to load the rest of the source messages for the given projection + /// A that is used to load the rest of the messages for the given projection /// pointer. /// IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Pointer projectionPointer, diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs index a7bb3de6..00909346 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs @@ -17,6 +17,11 @@ public interface IAnnotatedSourceData /// The time stamp of the source /// TimeStamp SourceTimeStamp { get; } + + /// + /// The id of the message + /// + Id MessageId { get; } /// /// The data diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs index bd839a40..ad83f309 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs @@ -18,13 +18,18 @@ public interface IAnnotatedSourceGroupData /// TimeStamp SourceTimeStamp { get; } + /// + /// The ids of the messages + /// + Id[] MessageIds { get; } + /// /// The data /// TData Data { get; } /// - /// The pointers to the entities + /// The pointers of the entities /// Pointer[] EntityPointers { get; } } diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index affecae5..07addce3 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -9,6 +9,11 @@ namespace EntityDb.Abstractions.Sources; /// public sealed record Message { + /// + /// The id assigned to the message. + /// + public required Id Id { get; init; } + /// /// A pointer to the entity /// diff --git a/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs index e1de7adb..898d4b3c 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs @@ -4,7 +4,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// -/// Abstracts a query on source messages. +/// Abstracts a query on messages. /// public interface IMessageQuery : IQuery { diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs index ab572cd7..53673da4 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs @@ -1,7 +1,7 @@ namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; /// -/// Builds a for a source message query. +/// Builds a for a message query. /// /// The type of sort used by the repository. public interface IMessageSortBuilder : ISortBuilder diff --git a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs index 749d63ab..881d508d 100644 --- a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs +++ b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs @@ -14,7 +14,7 @@ internal sealed class EntitySourceBuilder : IEntitySourceBuilder _knownEntities = new(); - private readonly List _sourceMessages = new(); + private readonly List _messages = new(); public EntitySourceBuilder(IAgent agent) { @@ -49,8 +49,9 @@ public IEntitySourceBuilder Append(Id entityId, object delta) var entity = _knownEntities[entityId].Reduce(delta); - _sourceMessages.Add(new Message + _messages.Add(new Message { + Id = Id.NewId(), EntityPointer = entity.GetPointer(), Delta = delta, AddLeases = delta is IAddLeasesDelta addLeasesDelta @@ -79,10 +80,10 @@ public Source Build(Id sourceId) Id = sourceId, TimeStamp = _agent.TimeStamp, AgentSignature = _agent.Signature, - Messages = _sourceMessages.ToImmutableArray(), + Messages = _messages.ToImmutableArray(), }; - _sourceMessages.Clear(); + _messages.Clear(); return source; } diff --git a/src/EntityDb.Common/EntityDb.Common.csproj b/src/EntityDb.Common/EntityDb.Common.csproj index 0fb988bd..de24f700 100644 --- a/src/EntityDb.Common/EntityDb.Common.csproj +++ b/src/EntityDb.Common/EntityDb.Common.csproj @@ -1,6 +1,5 @@  - EntityDb EventSourcing DDD CQRS A standard set of implementations (excluding persistence) of the EntityDb abstraction layer. @@ -9,6 +8,5 @@ - - + diff --git a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs index c26f8314..7e3e9504 100644 --- a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs @@ -70,11 +70,13 @@ CancellationToken cancellationToken .EnumerateAnnotatedAgentSignatures(query, cancellationToken) .SingleAsync(cancellationToken); - var sourceMessages = await sourceRepository + var messages = await sourceRepository .EnumerateAnnotatedDeltas(query, cancellationToken) - .Select(annotatedDeltas => new Message + .Select(annotatedDelta => new Message { - EntityPointer = annotatedDeltas.EntityPointer, Delta = annotatedDeltas.Data, + Id = annotatedDelta.MessageId, + EntityPointer = annotatedDelta.EntityPointer, + Delta = annotatedDelta.Data, }) .ToArrayAsync(cancellationToken); @@ -83,7 +85,7 @@ CancellationToken cancellationToken Id = annotatedAgentSignature.SourceId, TimeStamp = annotatedAgentSignature.SourceTimeStamp, AgentSignature = annotatedAgentSignature.Data, - Messages = sourceMessages.ToImmutableArray(), + Messages = messages.ToImmutableArray(), }; } } diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs index 2aaa8af2..5fb373e6 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs @@ -7,6 +7,7 @@ internal record AnnotatedSourceData ( Id SourceId, TimeStamp SourceTimeStamp, + Id MessageId, TData Data, Pointer EntityPointer ) : IAnnotatedSourceData @@ -15,6 +16,7 @@ public static IAnnotatedSourceData CreateFromBoxedData ( Id sourceId, TimeStamp sourceTimeStamp, + Id messageId, object boxedData, Pointer entityPointer ) @@ -26,6 +28,7 @@ Pointer entityPointer dataAnnotationType, sourceId, sourceTimeStamp, + messageId, boxedData, entityPointer )!; diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs index 0a0e393b..12d468a2 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs @@ -7,6 +7,7 @@ internal record AnnotatedSourceGroupData ( Id SourceId, TimeStamp SourceTimeStamp, + Id[] MessageIds, TData Data, Pointer[] EntityPointers ) : IAnnotatedSourceGroupData @@ -15,6 +16,7 @@ public static IAnnotatedSourceGroupData CreateFromBoxedData ( Id sourceId, TimeStamp sourceTimeStamp, + Id[] messageIds, object boxedData, Pointer[] entityPointers ) @@ -26,6 +28,7 @@ Pointer[] entityPointers dataAnnotationType, sourceId, sourceTimeStamp, + messageIds, boxedData, entityPointers )!; diff --git a/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs index 7275eb7f..313d4531 100644 --- a/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs +++ b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs @@ -78,6 +78,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken ( document.SourceId, document.SourceTimeStamp, + document.MessageId, envelopeService.Deserialize(document.Data), document.EntityPointer ); @@ -104,6 +105,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken ( document.SourceId, document.SourceTimeStamp, + document.MessageIds, envelopeService.Deserialize(document.Data), document.EntityPointers ); diff --git a/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs b/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs index 5c68f2b5..e53b5bf0 100644 --- a/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs +++ b/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs @@ -4,5 +4,6 @@ namespace EntityDb.Common.Sources.Documents; internal interface IMessageDocument : IDocument { + Id MessageId { get; } Pointer EntityPointer { get; } } diff --git a/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs b/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs index c3acc752..4b4040a5 100644 --- a/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs +++ b/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs @@ -4,5 +4,6 @@ namespace EntityDb.Common.Sources.Documents; internal interface IMessageGroupDocument : IDocument { + Id[] MessageIds { get; } Pointer[] EntityPointers { get; } } diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 3cb0c106..89348a75 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -21,6 +21,10 @@ public static InsertDocumentsCommand GetInsertCommand Source source ) { + var messageIds = source.Messages + .Select(message => message.Id) + .ToArray(); + var entityPointers = source.Messages .Select(message => message.EntityPointer) .Distinct() @@ -36,6 +40,7 @@ Source source { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, + MessageIds = messageIds, EntityIds = entityIds, EntityPointers = entityPointers, DataType = source.AgentSignature.GetType().Name, diff --git a/src/EntityDb.MongoDb/Documents/DeltaDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDocument.cs index f422b22b..8c48c814 100644 --- a/src/EntityDb.MongoDb/Documents/DeltaDocument.cs +++ b/src/EntityDb.MongoDb/Documents/DeltaDocument.cs @@ -43,6 +43,7 @@ Message message { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, + MessageId = message.Id, EntityId = message.EntityPointer.Id, EntityVersion = message.EntityPointer.Version, EntityPointer = message.EntityPointer, diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs index 2f08927a..f723331f 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs @@ -35,6 +35,7 @@ Message message { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, + MessageId = message.Id, EntityId = message.EntityPointer.Id, EntityVersion = message.EntityPointer.Version, EntityPointer = message.EntityPointer, diff --git a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs index 92c91dae..c1ea72b9 100644 --- a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs @@ -11,6 +11,8 @@ internal abstract record MessageDocumentBase : DocumentBase, IMessageDocument EntityPointerProjection { get; } = ProjectionBuilder.Include(nameof(EntityPointer)); + public required Id MessageId { get; init; } + public required Id EntityId { get; init; } public required Version EntityVersion { get; init; } public required Pointer EntityPointer { get; init; } diff --git a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs index 2dcf4693..d56da5b6 100644 --- a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs @@ -12,6 +12,8 @@ internal abstract record MessageGroupDocumentBase : DocumentBase, IMessageGroupD public static ProjectionDefinition EntityPointersProjection { get; } = ProjectionBuilder.Include(nameof(EntityPointers)); + + public required Id[] MessageIds { get; init; } public required Id[] EntityIds { get; init; } public required Pointer[] EntityPointers { get; init; } diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDocument.cs index 18daed8e..c001b56d 100644 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDocument.cs @@ -34,6 +34,7 @@ Message message { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, + MessageId = message.Id, EntityId = message.EntityPointer.Id, EntityVersion = message.EntityPointer.Version, EntityPointer = message.EntityPointer, diff --git a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs index 037ac709..7fed4968 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs @@ -14,10 +14,21 @@ private static readonly IndexKeysDefinitionBuilder private static readonly CreateIndexOptions UniquenessConstraint = new() { - Name = "Uniqueness Constraint", Unique = true, + Name = "Uniqueness Constraint", + Unique = true, + }; + + private static readonly CreateIndexOptions IdempotentConstraint = new() + { + Name = "Idempotent Constraint", + Unique = true, }; - private static readonly CreateIndexOptions LookupIndex = new() { Name = "Lookup Index" }; + private static readonly CreateIndexOptions LookupIndex = new() + { + Name = "Lookup Index", + Unique = false, + }; private static readonly Dictionary[]> SourceCollections = new() { @@ -34,6 +45,14 @@ private static readonly IndexKeysDefinitionBuilder }, [DeltaDocument.CollectionName] = new[] { + new CreateIndexModel + ( + IndexKeysBuilder.Combine + ( + IndexKeysBuilder.Descending(nameof(DeltaDocument.MessageId)) + ), + IdempotentConstraint + ), new CreateIndexModel ( IndexKeysBuilder.Combine diff --git a/src/EntityDb.Mvc/EntityDb.Mvc.csproj b/src/EntityDb.Mvc/EntityDb.Mvc.csproj index c6a9864f..0ab4b098 100644 --- a/src/EntityDb.Mvc/EntityDb.Mvc.csproj +++ b/src/EntityDb.Mvc/EntityDb.Mvc.csproj @@ -1,6 +1,5 @@  - EntityDb EventSourcing DDD CQRS MVC Provides a model for an AgentSignature that is mapped from an HttpContext. The source includes: Claims, Connection Information, and Raw Headers. diff --git a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj index c061428c..a798b7e5 100644 --- a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj +++ b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj @@ -1,11 +1,9 @@  - Exe - true entitydb-provisioner diff --git a/src/EntityDb.Redis/EntityDb.Redis.csproj b/src/EntityDb.Redis/EntityDb.Redis.csproj index 67a7da8d..481f2c0e 100644 --- a/src/EntityDb.Redis/EntityDb.Redis.csproj +++ b/src/EntityDb.Redis/EntityDb.Redis.csproj @@ -1,6 +1,5 @@  - EntityDb EventSourcing DDD CQRS Redis An implementation of the EntityDb Snapshot Repository interface, specifically for Redis. diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs index cfda5d7e..022883fe 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs @@ -20,6 +20,7 @@ public static IEnumerable CreateFromDeltas(Id entityId, uint n yield return new Message { + Id = Id.NewId(), EntityPointer = entityId + previousVersion, Delta = DeltaSeeder.Create(), }; diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs index 880923c2..1aa65866 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs @@ -24,9 +24,10 @@ public static Source Create(Id entityId, uint numDeltas, ulong previousVersionValue = 0) where TEntity : class, IEntity, ISnapshotWithTestLogic { - var sourceMessages = MessageSeeder - .CreateFromDeltas(entityId, numDeltas, previousVersionValue).ToArray(); + var messages = MessageSeeder + .CreateFromDeltas(entityId, numDeltas, previousVersionValue) + .ToArray(); - return Create(sourceMessages); + return Create(messages); } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index ec8fe39e..3166be78 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -640,6 +640,7 @@ private async Task var message = new Message { + Id = Id.NewId(), EntityPointer = Id.NewId() + Version.Zero, Delta = new DoNothing(), }; From dae6382136d359053feae2b9579574df64c7b0aa Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 63/93] refactor: replace all references to unused branch feature :D --- README.md | 6 ------ .../Entities/IEntitySourceBuilder.cs | 12 ++++++------ src/EntityDb.Abstractions/Projections/IProjection.cs | 2 +- .../Queries/FilterBuilders/IMessageFilterBuilder.cs | 10 +++++----- .../FilterBuilders/IMessageGroupFilterBuilder.cs | 6 +++--- src/EntityDb.Common/Entities/EntitySourceBuilder.cs | 2 +- ...dException.cs => EntityAlreadyLoadedException.cs} | 6 +++--- .../Sessions/RedisSnapshotSessionOptions.cs | 3 +-- .../Queries/{EntityBranchQuery.cs => EntityQuery.cs} | 2 +- .../Sources/SingleEntitySourceBuilderTests.cs | 6 +++--- test/EntityDb.Common.Tests/Sources/SourceTests.cs | 4 ++-- 11 files changed, 26 insertions(+), 33 deletions(-) rename src/EntityDb.Common/Exceptions/{EntityBranchAlreadyLoadedException.cs => EntityAlreadyLoadedException.cs} (59%) rename test/EntityDb.Common.Tests/Implementations/Queries/{EntityBranchQuery.cs => EntityQuery.cs} (93%) diff --git a/README.md b/README.md index 3f2bd44d..0dc6d54e 100644 --- a/README.md +++ b/README.md @@ -78,12 +78,6 @@ violate the uniqueness constraint, it will be rejected. (This is obnoxious behav should check before attempting to commit to see if the username is available and give immediate feedback to choose a different username). -### Aliases - -An alias is like a lease, except that it doesn't have a scope or a label. It is -unique per entity branch name and a value, and it can be used to achieve idempotency automatically. -Source repositories will skip messages if its alias is already recorded. - ### Snapshots A snapshot is a stateful object at a given point in time. They always have an identifier and a version. diff --git a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs index 63ecf5fa..c0f03f84 100644 --- a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs +++ b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs @@ -11,14 +11,14 @@ namespace EntityDb.Abstractions.Entities; public interface IEntitySourceBuilder { /// - /// Returns a associated with a given branch, if it is known. + /// Returns a associated with a given id, if it is known. /// /// The id associated with the entity. /// A associated with , if it is known. TEntity GetEntity(Id entityId); /// - /// Indicates whether or not a associated with a given branch is in memory. + /// Indicates whether or not a associated with a given id is in memory. /// /// The id of the entity. /// @@ -28,9 +28,9 @@ public interface IEntitySourceBuilder bool IsEntityKnown(Id entityId); /// - /// Associate a with a given entity branch. + /// Associate a with a given entity id. /// - /// A branch associated with a . + /// A id associated with a . /// A . /// The source builder. /// @@ -40,9 +40,9 @@ public interface IEntitySourceBuilder IEntitySourceBuilder Load(Id entityId, TEntity entity); /// - /// Adds a single delta to the source with a given entity branch. + /// Adds a single delta to the source with a given entity id. /// - /// The branch associated with the . + /// The id associated with the . /// The new delta that modifies the . /// The source builder. IEntitySourceBuilder Append(Id entityId, object delta); diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index 9146872a..e267123c 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -34,6 +34,6 @@ IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Poin /// Maps a source to a set of entity ids. May be empty if the source does not map to the projection. /// /// A source - /// The entity branches for the projections. + /// The entity ids for the projections. static abstract IEnumerable EnumerateEntityIds(Source source); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs index 94290054..dfb7589d 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs @@ -13,12 +13,12 @@ public interface IMessageFilterBuilder : IFilterBuilder { /// /// Returns a that only includes objects with an entity - /// branch in a set of entity branches. + /// id in a set of entity ids. /// - /// The entity branches. + /// The entity ids. /// /// Returns a that only includes objects with an entity - /// branch in . + /// id in . /// TFilter EntityIdIn(params Id[] entityIds); @@ -26,7 +26,7 @@ public interface IMessageFilterBuilder : IFilterBuilder /// Returns a that only includes objects with an entity /// version greater than or equal to a specific entity version. /// - /// The entity branches. + /// The entity ids. /// /// Returns a that only includes objects with an entity /// version greater than or equal to . @@ -37,7 +37,7 @@ public interface IMessageFilterBuilder : IFilterBuilder /// Returns a that only includes objects with an entity /// version greater than or equal to a specific entity version. /// - /// The entity branches. + /// The entity ids. /// /// Returns a that only includes objects with an entity /// version less than or equal to . diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs index dc1e2e40..a364e644 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs @@ -12,12 +12,12 @@ public interface IMessageGroupFilterBuilder : IFilterBuilder { /// /// Returns a that only includes objects with any entity - /// branch in a set of entity branches. + /// id in a set of entity ids. /// - /// The entity branches. + /// The entity ids. /// /// Returns a that only includes objects with any entity - /// branch in . + /// id in . /// TFilter AnyEntityIdIn(params Id[] entityIds); } diff --git a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs index 881d508d..9534f31b 100644 --- a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs +++ b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs @@ -35,7 +35,7 @@ public IEntitySourceBuilder Load(Id entityId, TEntity entity) { if (IsEntityKnown(entityId)) { - throw new EntityBranchAlreadyLoadedException(); + throw new EntityAlreadyLoadedException(); } _knownEntities.Add(entityId, entity); diff --git a/src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs b/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs similarity index 59% rename from src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs rename to src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs index 29d5e43e..32ec6f48 100644 --- a/src/EntityDb.Common/Exceptions/EntityBranchAlreadyLoadedException.cs +++ b/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs @@ -3,10 +3,10 @@ namespace EntityDb.Common.Exceptions; /// -/// The exception that is thrown when an actor passes an entity branch to +/// The exception that is thrown when an actor passes an entity id to /// -/// with an entity branch that has already been loaded. +/// with an entity id that has already been loaded. /// -public sealed class EntityBranchAlreadyLoadedException : Exception +public sealed class EntityAlreadyLoadedException : Exception { } diff --git a/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs index fca686ae..28366608 100644 --- a/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs @@ -16,8 +16,7 @@ public sealed class RedisSnapshotSessionOptions /// /// Choose a key namespace for snapshots. Snapshots are stored with keys in the following format: - /// {KeyNamespace}#{SnapshotId}@{SnapshotVersion} or - /// {KeyNamespace}#{SnapshotId}/{SnapshotBranchName}@{SnapshotVersion} + /// {KeyNamespace}#{SnapshotId}@{SnapshotVersion} /// public string KeyNamespace { get; set; } = default!; diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityBranchQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs similarity index 93% rename from test/EntityDb.Common.Tests/Implementations/Queries/EntityBranchQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs index bb3d7e2b..74e1f6fa 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityBranchQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Queries; -public record EntityBranchQuery(Id EntityId, object? Options = null) : IMessageGroupQuery, IMessageQuery, +public record EntityQuery(Id EntityId, object? Options = null) : IMessageGroupQuery, IMessageQuery, ILeaseQuery, ITagQuery { public TFilter GetFilter(ILeaseFilterBuilder builder) diff --git a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs index 993ab2c4..319afc24 100644 --- a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs @@ -68,12 +68,12 @@ private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpected // ACT - var actualEntityBranch = sourceBuilder.EntityId; + var actualEntityId = sourceBuilder.EntityId; var actualEntity = sourceBuilder.GetEntity(); // ASSERT - actualEntityBranch.ShouldBe(expectedEntityId); + actualEntityId.ShouldBe(expectedEntityId); actualEntity.ShouldBe(expectedEntity); } @@ -144,7 +144,7 @@ private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_T // ASSERT - Should.Throw(() => { sourceBuilder.Load(entity); }); + Should.Throw(() => { sourceBuilder.Load(entity); }); } private async Task diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 3166be78..cdbaba2e 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -826,7 +826,7 @@ private async Task var sourceCommitted = await sourceRepository.Commit(source); - var query = new EntityBranchQuery(expectedEntityId); + var query = new EntityQuery(expectedEntityId); // ARRANGE ASSERTIONS @@ -1358,7 +1358,7 @@ private async Task entityId.ShouldNotBeNull(); - var query = new EntityBranchQuery(entityId.Value); + var query = new EntityQuery(entityId.Value); await PutSources(serviceScope, sources); await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); From 4234985349a34342bd6fb351381bd7399d8254f5 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 64/93] chore: clean up words --- .../Entities/IEntityRepository.cs | 2 +- .../Sources/SingleEntitySourceBuilderTests.cs | 16 ++++--------- .../Sources/SourceTests.cs | 24 +++++++++---------- .../Sources/TryCatchSourceRepositoryTests.cs | 4 ++-- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IEntityRepository.cs index 90d99d73..0e2b8d10 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepository.cs @@ -34,6 +34,6 @@ public interface IEntityRepository : IDisposableResource /// /// The source. /// A cancellation token. - /// true if the insert succeeded, or false if the insert failed. + /// true if the commit succeeded, or false if the commit failed. Task Commit(Source source, CancellationToken cancellationToken = default); } diff --git a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs index 319afc24..8bfde9a1 100644 --- a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs @@ -78,7 +78,7 @@ private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpected } private async Task - Generic_GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenSourceDoesInsertLeases( + Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( EntityAdder entityAdder) where TEntity : IEntity { @@ -99,16 +99,10 @@ private async Task .Append(new AddLease(new Lease(default!, default!, default!))) .Build(default); - var entity = sourceBuilder.GetEntity(); - // ASSERT source.Messages.Length.ShouldBe(1); - - var addLeasesDelta = - source.Messages[0].Delta.ShouldBeAssignableTo>().ShouldNotBeNull(); - - addLeasesDelta.GetLeases(entity).ShouldNotBeEmpty(); + source.Messages[0].AddLeases.Length.ShouldBe(1); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( @@ -148,7 +142,7 @@ private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_T } private async Task - Generic_GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionAutoIncrements( + Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( EntityAdder entityAdder) where TEntity : IEntity { @@ -249,7 +243,7 @@ public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(EntityAd [Theory] [MemberData(nameof(AddEntity))] - public Task GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenSourceDoesInsertLeases( + public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( EntityAdder entityAdder) { return RunGenericTestAsync @@ -272,7 +266,7 @@ public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(E [Theory] [MemberData(nameof(AddEntity))] - public Task GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionAutoIncrements( + public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( EntityAdder entityAdder) { return RunGenericTestAsync diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index cdbaba2e..1b82b58b 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -525,11 +525,11 @@ private async Task // ACT - var inserted = await sourceRepository.Commit(source); + var committed = await sourceRepository.Commit(source); // ASSERT - inserted.ShouldBeFalse(); + committed.ShouldBeFalse(); loggerVerifier.Invoke(Times.Once()); } @@ -578,7 +578,7 @@ private async Task Generic_GivenNonUniqueSourceIds_WhenCommittingSources_ThenSec secondSourceCommitted.ShouldBeFalse(); } - private async Task Generic_GivenNonUniqueVersions_WhenInsertingDeltas_ThenReturnFalse( + private async Task Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse( SourcesAdder sourcesAdder, EntityAdder entityAdder) where TEntity : IEntity { @@ -626,7 +626,7 @@ private async Task Generic_GivenNonUniqueVersions_WhenInsertingDeltas_ThenReturn } private async Task - Generic_GivenVersionZero_WhenInsertingDeltas_ThenSourceIsCommitted( + Generic_GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue( SourcesAdder sourcesAdder, EntityAdder entityAdder) where TEntity : IEntity { @@ -662,7 +662,7 @@ private async Task } private async Task - Generic_GivenNonUniqueVersions_WhenInsertingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( + Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( SourcesAdder sourcesAdder, EntityAdder entityAdder) where TEntity : IEntity { @@ -726,7 +726,7 @@ private async Task loggerVerifier.Invoke(Times.Once()); } - private async Task Generic_GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue( + private async Task Generic_GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue( SourcesAdder sourcesAdder, EntityAdder entityAdder) where TEntity : IEntity { @@ -761,7 +761,7 @@ private async Task Generic_GivenNonUniqueTags_WhenInsertingTagDocuments_ThenRetu sourceCommitted.ShouldBeTrue(); } - private async Task Generic_GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFalse( + private async Task Generic_GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse( SourcesAdder sourcesAdder, EntityAdder entityAdder) where TEntity : IEntity { @@ -1506,7 +1506,7 @@ public Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFa [Theory] [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueVersions_WhenInsertingDeltas_ThenReturnFalse(SourcesAdder sourcesAdder, + public Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(SourcesAdder sourcesAdder, EntityAdder entityAdder) { return RunGenericTestAsync @@ -1518,7 +1518,7 @@ public Task GivenNonUniqueVersions_WhenInsertingDeltas_ThenReturnFalse(SourcesAd [Theory] [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenVersionZero_WhenInsertingDeltas_ThenSourceIsCommitted( + public Task GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue( SourcesAdder sourcesAdder, EntityAdder entityAdder) { return RunGenericTestAsync @@ -1530,7 +1530,7 @@ public Task GivenVersionZero_WhenInsertingDeltas_ThenSourceIsCommitted( [Theory] [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueVersions_WhenInsertingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( + public Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( SourcesAdder sourcesAdder, EntityAdder entityAdder) { return RunGenericTestAsync @@ -1542,7 +1542,7 @@ public Task GivenNonUniqueVersions_WhenInsertingDeltas_ThenOptimisticConcurrency [Theory] [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue(SourcesAdder sourcesAdder, + public Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourcesAdder sourcesAdder, EntityAdder entityAdder) { return RunGenericTestAsync @@ -1554,7 +1554,7 @@ public Task GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue(SourcesA [Theory] [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFalse(SourcesAdder sourcesAdder, + public Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse(SourcesAdder sourcesAdder, EntityAdder entityAdder) { return RunGenericTestAsync diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs index 925102e4..ba2393a9 100644 --- a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -125,7 +125,7 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti await tryCatchSourceRepository.EnumerateTags(default!).ToArrayAsync(); var annotatedDeltas = await tryCatchSourceRepository.EnumerateAnnotatedDeltas(default!).ToArrayAsync(); - var inserted = + var committed = await tryCatchSourceRepository.Commit(default!); // ASSERT @@ -143,7 +143,7 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti leases.ShouldBeEmpty(); tags.ShouldBeEmpty(); annotatedDeltas.ShouldBeEmpty(); - inserted.ShouldBeFalse(); + committed.ShouldBeFalse(); loggerVerifier.Invoke(Times.Exactly(14)); } } \ No newline at end of file From 6b3717ea676fbbb4329bb8d71d5b162d60ee492e Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 65/93] fix: use interface in doc comment --- src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 7e3353ef..d7a2dfe4 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -143,7 +143,7 @@ public static void AddAgentAccessor(this IServiceCollection serv } /// - /// Adds a transient and a transient implementation of + /// Adds a transient and a transient implementation of /// to a service collection. /// /// The service collection. From b1fd33ed21b9176d6894ec6827f556e8c2c03475 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 66/93] format --- .../Annotations/IAnnotatedSourceData.cs | 2 +- .../Annotations/IAnnotatedSourceGroupData.cs | 2 +- src/EntityDb.Abstractions/Sources/Message.cs | 2 +- src/EntityDb.Common/EntityDb.Common.csproj | 2 +- .../Documents/AgentSignatureDocument.cs | 2 +- .../Documents/MessageDocumentBase.cs | 4 ++-- .../Documents/MessageGroupDocumentBase.cs | 4 ++-- .../Extensions/MongoClientExtensions.cs | 14 ++++------- .../RedisSnapshotRepositoryFactory.cs | 1 - .../Sources/SingleEntitySourceBuilderTests.cs | 1 - .../Sources/SourceTests.cs | 24 +++++++++---------- test/EntityDb.Common.Tests/TestsBase.cs | 22 ++++++++++------- 12 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs index 00909346..a708b163 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs @@ -17,7 +17,7 @@ public interface IAnnotatedSourceData /// The time stamp of the source /// TimeStamp SourceTimeStamp { get; } - + /// /// The id of the message /// diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs index ad83f309..d371d81d 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs @@ -22,7 +22,7 @@ public interface IAnnotatedSourceGroupData /// The ids of the messages /// Id[] MessageIds { get; } - + /// /// The data /// diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index 07addce3..e9cc072f 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -13,7 +13,7 @@ public sealed record Message /// The id assigned to the message. /// public required Id Id { get; init; } - + /// /// A pointer to the entity /// diff --git a/src/EntityDb.Common/EntityDb.Common.csproj b/src/EntityDb.Common/EntityDb.Common.csproj index de24f700..866acc40 100644 --- a/src/EntityDb.Common/EntityDb.Common.csproj +++ b/src/EntityDb.Common/EntityDb.Common.csproj @@ -8,5 +8,5 @@ - + diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 89348a75..86eca333 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -24,7 +24,7 @@ Source source var messageIds = source.Messages .Select(message => message.Id) .ToArray(); - + var entityPointers = source.Messages .Select(message => message.EntityPointer) .Distinct() diff --git a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs index c1ea72b9..484af3c5 100644 --- a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs @@ -11,9 +11,9 @@ internal abstract record MessageDocumentBase : DocumentBase, IMessageDocument EntityPointerProjection { get; } = ProjectionBuilder.Include(nameof(EntityPointer)); - public required Id MessageId { get; init; } - public required Id EntityId { get; init; } public required Version EntityVersion { get; init; } + + public required Id MessageId { get; init; } public required Pointer EntityPointer { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs index d56da5b6..2fbd6204 100644 --- a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs @@ -12,9 +12,9 @@ internal abstract record MessageGroupDocumentBase : DocumentBase, IMessageGroupD public static ProjectionDefinition EntityPointersProjection { get; } = ProjectionBuilder.Include(nameof(EntityPointers)); - - public required Id[] MessageIds { get; init; } public required Id[] EntityIds { get; init; } + + public required Id[] MessageIds { get; init; } public required Pointer[] EntityPointers { get; init; } } diff --git a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs index 7fed4968..16e200d0 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs @@ -14,21 +14,15 @@ private static readonly IndexKeysDefinitionBuilder private static readonly CreateIndexOptions UniquenessConstraint = new() { - Name = "Uniqueness Constraint", - Unique = true, + Name = "Uniqueness Constraint", Unique = true, }; - + private static readonly CreateIndexOptions IdempotentConstraint = new() { - Name = "Idempotent Constraint", - Unique = true, + Name = "Idempotent Constraint", Unique = true, }; - private static readonly CreateIndexOptions LookupIndex = new() - { - Name = "Lookup Index", - Unique = false, - }; + private static readonly CreateIndexOptions LookupIndex = new() { Name = "Lookup Index", Unique = false }; private static readonly Dictionary[]> SourceCollections = new() { diff --git a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs index cee3f3d1..dd32286a 100644 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs +++ b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs @@ -4,7 +4,6 @@ using EntityDb.Common.Snapshots; using EntityDb.Redis.ConnectionMultiplexers; using EntityDb.Redis.Snapshots.Sessions; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace EntityDb.Redis.Snapshots; diff --git a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs index 8bfde9a1..c38fe736 100644 --- a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Entities.Deltas; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Attributes; diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 1b82b58b..5e5be576 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -126,7 +126,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -153,7 +153,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, @@ -180,7 +180,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -207,7 +207,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -234,7 +234,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -263,7 +263,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -292,7 +292,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -321,7 +321,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; Id[] GetExpectedResults(bool invert) @@ -350,7 +350,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; object[] GetExpectedResults(bool invert) @@ -377,7 +377,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; object[] GetExpectedResults(bool invert) @@ -404,7 +404,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; ILease[] GetExpectedResults(bool invert) @@ -431,7 +431,7 @@ ExpectedObjects expectedObjects { await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); - + return; IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 2b1eae64..2fbf44e3 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -208,11 +208,14 @@ private static IEnumerable AllEntitySnapshotAdders() return from snapshotAdder in AllSnapshotAdders() let entityAdder = GetEntityAdder() - select snapshotAdder with { AddDependencies = snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => + select snapshotAdder with { - serviceCollection.AddEntitySnapshotSourceSubscriber(TestSessionOptions.ReadOnly, - TestSessionOptions.Write); - }) }; + AddDependencies = snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => + { + serviceCollection.AddEntitySnapshotSourceSubscriber(TestSessionOptions.ReadOnly, + TestSessionOptions.Write); + }), + }; } private static IEnumerable AllEntityAdders() @@ -230,11 +233,14 @@ private static IEnumerable AllProjectionAdders() where TProjection : class, IProjection, ISnapshotWithTestLogic { return AllSnapshotAdders() - .Select(snapshotAdder => snapshotAdder with { AddDependencies = snapshotAdder.AddDependencies + (serviceCollection => + .Select(snapshotAdder => snapshotAdder with { - serviceCollection.AddProjection(); - serviceCollection.AddProjectionSnapshotSourceSubscriber(TestSessionOptions.Write); - }) } + AddDependencies = snapshotAdder.AddDependencies + (serviceCollection => + { + serviceCollection.AddProjection(); + serviceCollection.AddProjectionSnapshotSourceSubscriber(TestSessionOptions.Write); + }), + } ); } From 464d836e0227cc2d606dc55c2cf783e8265d5354 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 67/93] chore: better var names --- test/EntityDb.Common.Tests/Sources/SourceTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 5e5be576..522ed735 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -1089,7 +1089,7 @@ private async Task var sourceCommitted = await sourceRepository.Commit(source); - var newCommands = await sourceRepository.EnumerateDeltas(versionOneQuery).ToArrayAsync(); + var newDeltas = await sourceRepository.EnumerateDeltas(versionOneQuery).ToArrayAsync(); // ASSERT @@ -1099,9 +1099,9 @@ private async Task source.Messages[0].EntityPointer.Version.ShouldBe(new Version(1)); - newCommands.Length.ShouldBe(1); + newDeltas.Length.ShouldBe(1); - newCommands[0].ShouldBeEquivalentTo(expectedDelta); + newDeltas[0].ShouldBeEquivalentTo(expectedDelta); } private async Task @@ -1146,7 +1146,7 @@ private async Task var secondSourceCommitted = await sourceRepository.Commit(secondSource); - var newCommands = await sourceRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); + var newDeltas = await sourceRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); // ASSERT @@ -1156,9 +1156,9 @@ private async Task secondSource.Messages[0].EntityPointer.Version.ShouldBe(new Version(2)); - newCommands.Length.ShouldBe(1); + newDeltas.Length.ShouldBe(1); - newCommands[0].ShouldBeEquivalentTo(expectedDelta); + newDeltas[0].ShouldBeEquivalentTo(expectedDelta); } private async Task From 0d7f829c6a04045491bc7decbb089f4a16d00650 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 68/93] refactor: pull some mocking logic to be re-usable with a more complicated repository mock --- test/EntityDb.Common.Tests/TestsBase.cs | 47 +++++++++++++++++++------ 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 2fbf44e3..245453d5 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -391,21 +391,25 @@ void Verifier(Times times) } } - protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( - object[]? deltas = null) + protected static void SetupSourceRepositoryMock(Mock sourceRepositoryMock, + List committedSources) { - deltas ??= Array.Empty(); - - var sourceRepositoryMock = new Mock(MockBehavior.Strict); + } + protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( + Mock sourceRepositoryMock, List? committedSources = null) + { sourceRepositoryMock - .Setup(repository => - repository.Commit(It.IsAny(), It.IsAny())) - .ReturnsAsync(true); + .Setup(repository => repository.Commit(It.IsAny(), It.IsAny())) + .ReturnsAsync((Source source, CancellationToken _) => + { + committedSources?.Add(source); + + return true; + }); sourceRepositoryMock - .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerablePolyfill.FromResult(deltas)); + .Setup(repository => repository.Dispose()); sourceRepositoryMock .Setup(repository => repository.DisposeAsync()) @@ -421,9 +425,32 @@ protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( sourceRepositoryFactoryMock .Setup(factory => factory.Dispose()); + sourceRepositoryFactoryMock + .Setup(factory => factory.DisposeAsync()) + .Returns(ValueTask.CompletedTask); + return sourceRepositoryFactoryMock.Object; } + protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( + object[]? deltas = null) + { + deltas ??= Array.Empty(); + + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + sourceRepositoryMock + .Setup(repository => + repository.Commit(It.IsAny(), It.IsAny())) + .ReturnsAsync(true); + + sourceRepositoryMock + .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(deltas)); + + return GetMockedSourceRepositoryFactory(sourceRepositoryMock, new List()); + } + protected static ISnapshotRepositoryFactory GetMockedSnapshotRepositoryFactory ( TEntity? snapshot = default From bc7973b26c9d5859450c6b5c6f3a222a2c67a630 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 69/93] feat: Event Streams --- .../EventStreams/IEventStreamRepository.cs | 39 ++ .../IEventStreamRepositoryFactory.cs | 17 + src/EntityDb.Abstractions/ValueObjects/Key.cs | 14 + .../EventStreams/EventStreamRepository.cs | 139 ++++++++ .../EventStreamRepositoryFactory.cs | 40 +++ .../Extensions/ServiceCollectionExtensions.cs | 12 + .../Queries/Standard/MatchingLeaseQuery.cs | 30 ++ .../Queries/Standard/MatchingLeasesQuery.cs | 34 ++ .../EventStreams/EventStreamTests.cs | 335 ++++++++++++++++++ test/EntityDb.Common.Tests/TestsBase.cs | 6 + 10 files changed, 666 insertions(+) create mode 100644 src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs create mode 100644 src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs create mode 100644 src/EntityDb.Abstractions/ValueObjects/Key.cs create mode 100644 src/EntityDb.Common/EventStreams/EventStreamRepository.cs create mode 100644 src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs create mode 100644 test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs diff --git a/src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs b/src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs new file mode 100644 index 00000000..743036b6 --- /dev/null +++ b/src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs @@ -0,0 +1,39 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.EventStreams; + +/// +/// Encapsulates the source repository and manages +/// +public interface IEventStreamRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// Stages a single delta with a given stream id and event id. + /// + /// The key associated with the stream. + /// A key associated with the event. + /// The new delta that modifies the event stream. + /// A cancellation token. + /// Only false if the event key already exists. + /// + /// If a duplicate event key is received for a given stream key, the delta + /// will be skipped. + /// + Task Stage(Key streamKey, Key eventKey, object delta, CancellationToken cancellationToken = default); + + /// + /// Commits all stages deltas. + /// + /// A new id for the new source. + /// How many attempts should be made to commit the source? + /// A cancellation token + /// true if the commit succeeded, or false if the commit failed. + Task Commit(Id sourceId, byte maxAttempts = 3, CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs b/src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs new file mode 100644 index 00000000..bcfa124d --- /dev/null +++ b/src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs @@ -0,0 +1,17 @@ +namespace EntityDb.Abstractions.EventStreams; + +/// +/// Represents a type used to create instances of . +/// +public interface IEventStreamRepositoryFactory +{ + /// + /// Creates a new instance of . + /// + /// The name of the agent signature options. + /// The name of the source session options. + /// A cancellation token. + /// A new instance of . + Task CreateRepository(string agentSignatureOptionsName, + string sourceSessionOptionsName, CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/ValueObjects/Key.cs b/src/EntityDb.Abstractions/ValueObjects/Key.cs new file mode 100644 index 00000000..f68315f0 --- /dev/null +++ b/src/EntityDb.Abstractions/ValueObjects/Key.cs @@ -0,0 +1,14 @@ +namespace EntityDb.Abstractions.ValueObjects; + +/// +/// Represents a key for an event stream. +/// +/// The backing value. +public readonly record struct Key(string Value) +{ + /// + public override string ToString() + { + return Value; + } +} diff --git a/src/EntityDb.Common/EventStreams/EventStreamRepository.cs b/src/EntityDb.Common/EventStreams/EventStreamRepository.cs new file mode 100644 index 00000000..78b66605 --- /dev/null +++ b/src/EntityDb.Common/EventStreams/EventStreamRepository.cs @@ -0,0 +1,139 @@ +using EntityDb.Abstractions.EventStreams; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Sources.Attributes; +using EntityDb.Common.Sources.Queries.Standard; +using System.Collections.Immutable; + +namespace EntityDb.Common.EventStreams; + +internal sealed class EventStreamRepository : DisposableResourceBaseClass, IEventStreamRepository +{ + private const string RootScope = "EventStream"; + private const string StreamIdLabel = "StreamId"; + private const string EventIdLabel = "EventId"; + private readonly IAgent _agent; + private readonly Dictionary _knownEntityIds = new(); + private readonly List _messages = new(); + + public EventStreamRepository(IAgent agent, ISourceRepository sourceRepository) + { + _agent = agent; + + SourceRepository = sourceRepository; + } + + public ISourceRepository SourceRepository { get; } + + public async Task Stage(Key streamKey, Key eventKey, object delta, + CancellationToken cancellationToken = default) + { + var eventKeyLease = GetEventKeyLease(streamKey, eventKey); + + var entityPointer = await GetEntityPointer(eventKeyLease, cancellationToken); + + if (entityPointer != default) + { + return false; + } + + var addLeases = new List { eventKeyLease }; + + var (found, entityId) = await GetEntityId(streamKey, cancellationToken); + + if (!found) + { + addLeases.Add(GetStreamKeyLease(streamKey)); + } + + _messages.Add(new Message + { + Id = Id.NewId(), EntityPointer = entityId, Delta = delta, AddLeases = addLeases.ToImmutableArray(), + }); + + return true; + } + + public async Task Commit(Id sourceId, byte maxAttempts = 3, CancellationToken cancellationToken = default) + { + var source = new Source + { + Id = sourceId, + TimeStamp = _agent.TimeStamp, + AgentSignature = _agent.Signature, + Messages = _messages.ToImmutableArray(), + }; + + _messages.Clear(); + + byte attempt = 1; + + while (true) + { + var committed = await SourceRepository.Commit(source, cancellationToken); + + if (committed) + { + return committed; + } + + attempt += 1; + + if (attempt == maxAttempts) + { + break; + } + + await Task.Delay(100 * attempt, cancellationToken); + } + + return false; + } + + public override ValueTask DisposeAsync() + { + return SourceRepository.DisposeAsync(); + } + + public static ILease GetStreamKeyLease(Key streamKey) + { + return new Lease(RootScope, StreamIdLabel, streamKey.Value); + } + + public static ILease GetEventKeyLease(Key streamKey, Key eventKey) + { + return new Lease($"{RootScope}/{streamKey}", EventIdLabel, eventKey.Value); + } + + private async Task GetEntityPointer(ILease lease, CancellationToken cancellationToken) + { + var query = new MatchingLeaseQuery(lease); + + return await SourceRepository + .EnumerateEntityPointers(query, cancellationToken) + .SingleOrDefaultAsync(cancellationToken); + } + + private async Task<(bool, Id)> GetEntityId(Key streamKey, CancellationToken cancellationToken) + { + if (_knownEntityIds.TryGetValue(streamKey, out var entityId)) + { + return (true, entityId); + } + + var streamKeyLease = GetStreamKeyLease(streamKey); + + var entityPointer = await GetEntityPointer(streamKeyLease, cancellationToken); + + var found = entityPointer != default; + + entityId = found ? entityPointer.Id : Id.NewId(); + + _knownEntityIds.Add(streamKey, entityId); + + return (found, entityId); + } +} diff --git a/src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs b/src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs new file mode 100644 index 00000000..040abd29 --- /dev/null +++ b/src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs @@ -0,0 +1,40 @@ +using EntityDb.Abstractions.EventStreams; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using Microsoft.Extensions.DependencyInjection; + +namespace EntityDb.Common.EventStreams; + +internal sealed class EventStreamRepositoryFactory : IEventStreamRepositoryFactory +{ + private readonly IAgentAccessor _agentAccessor; + private readonly IServiceProvider _serviceProvider; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + + public EventStreamRepositoryFactory + ( + IServiceProvider serviceProvider, + IAgentAccessor agentAccessor, + ISourceRepositoryFactory sourceRepositoryFactory + ) + { + _serviceProvider = serviceProvider; + _agentAccessor = agentAccessor; + _sourceRepositoryFactory = sourceRepositoryFactory; + } + + public async Task CreateRepository + ( + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken + ) + { + var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); + + var sourceRepository = await _sourceRepositoryFactory + .CreateRepository(sourceSessionOptionsName, cancellationToken); + + return ActivatorUtilities.CreateInstance(_serviceProvider, agent, sourceRepository); + } +} diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index d7a2dfe4..34617f14 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -1,8 +1,10 @@ using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.EventStreams; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; using EntityDb.Common.Entities; +using EntityDb.Common.EventStreams; using EntityDb.Common.Projections; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; @@ -174,6 +176,16 @@ public static void AddEntitySnapshotSourceSubscriber(this IServiceColle snapshotSessionOptionsName)); } + /// + /// Adds a transient implementation of + /// to a service collection. + /// + /// The service collection. + public static void AddEventStream(this IServiceCollection serviceCollection) + { + serviceCollection.AddTransient(); + } + /// /// Adds projections for . /// diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs new file mode 100644 index 00000000..27778572 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs @@ -0,0 +1,30 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record MatchingLeaseQuery(ILease Lease) : ILeaseQuery +{ + public TFilter GetFilter(ILeaseFilterBuilder builder) + { + return builder.And + ( + builder.LeaseScopeEq(Lease.Scope), + builder.LeaseLabelEq(Lease.Label), + builder.LeaseValueEq(Lease.Value) + ); + } + + public TSort? GetSort(ILeaseSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; + + public object? Options => null; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs new file mode 100644 index 00000000..e5ee045f --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs @@ -0,0 +1,34 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record MatchingLeasesQuery(params ILease[] Leases) : ILeaseQuery +{ + public TFilter GetFilter(ILeaseFilterBuilder builder) + { + return builder.Or + ( + Leases.Select(lease => builder.And + ( + builder.LeaseScopeEq(lease.Scope), + builder.LeaseLabelEq(lease.Label), + builder.LeaseValueEq(lease.Value) + )) + .ToArray() + ); + } + + public TSort? GetSort(ILeaseSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; + + public object? Options => null; +} diff --git a/test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs b/test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs new file mode 100644 index 00000000..823d8760 --- /dev/null +++ b/test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs @@ -0,0 +1,335 @@ +using EntityDb.Abstractions.EventStreams; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.EventStreams; +using EntityDb.Common.Extensions; +using EntityDb.Common.Polyfills; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.Common.Tests.Implementations.Deltas; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Shouldly; +using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.EventStreams; + +[Collection(nameof(DatabaseContainerCollection))] +public class EventStreamTests : TestsBase +{ + public EventStreamTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) + : base(serviceProvider, databaseContainerFixture) + { + } + + [Fact] + public async Task GivenNewEventStreamMock_WhenStagingNewEventKey_ThenCommittedSourceIsCorrect() + { + // ARRANGE + + var streamKey = new Key("StreamKey"); + var expectedStreamKeyLease = EventStreamRepository.GetStreamKeyLease(streamKey); + + var eventKey = new Key("EventKey"); + var expectedEventKeyLease = EventStreamRepository.GetEventKeyLease(streamKey, eventKey); + + var expectedDelta = new DoNothing(); + + var committedSources = new List(); + + var sequenceMock = new MockSequence(); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + // First query checks if eventKey lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerable.Empty()); + + // Second query checks if streamKey lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerable.Empty()); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock, committedSources)); + + serviceCollection.AddEventStream(); + }); + + await using var eventStreamRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(default!, default!); + + // ACT + + var staged = await eventStreamRepository + .Stage(streamKey, eventKey, expectedDelta); + + var committed = await eventStreamRepository + .Commit(Id.NewId()); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].EntityPointer.Version.ShouldBe(Version.Zero); + committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(2); + committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedEventKeyLease); + committedSources[0].Messages[0].AddLeases[1].ShouldBe(expectedStreamKeyLease); + } + + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenNewEventStream_WhenStagingNewEventKey_ThenCommitReturnsTrue(SourcesAdder sourcesAdder) + { + // ARRANGE + + var streamKey = new Key("StreamKey"); + var streamKeyLease = EventStreamRepository.GetStreamKeyLease(streamKey); + + var eventKey = new Key("EventKey"); + var eventKeyLease = EventStreamRepository.GetEventKeyLease(streamKey, eventKey); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.AddEventStream(); + }); + + await using var eventStreamRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(default!, default!); + + // ACT + + var staged = await eventStreamRepository + .Stage(streamKey, eventKey, new DoNothing()); + + var committed = await eventStreamRepository + .Commit(Id.NewId()); + + var entityPointerCount = await eventStreamRepository.SourceRepository + .EnumerateEntityPointers(new MatchingLeasesQuery(streamKeyLease, eventKeyLease)) + .CountAsync(); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + entityPointerCount.ShouldBe(1); + } + + [Fact] + public async Task GivenExistingEventStreamMock_WhenStagingNewEventKey_ThenCommittedSourceIsCorrect() + { + // ARRANGE + + var entityPointer = Id.NewId() + Version.Zero.Next(); + var streamKey = new Key("StreamKey"); + var eventKey = new Key("EventKey"); + var expectedLease = EventStreamRepository.GetEventKeyLease(streamKey, eventKey); + + var committedSources = new List(); + + var sequenceMock = new MockSequence(); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + // First query checks if eventKey lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerable.Empty()); + + // Second query checks if streamKey lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(new[] { entityPointer })); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock, committedSources)); + + serviceCollection.AddEventStream(); + }); + + await using var eventStreamRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(default!, default!); + + // ARRANGE ASSERTIONS + + entityPointer.Version.ShouldNotBe(Version.Zero); + + // ACT + + var staged = await eventStreamRepository + .Stage(streamKey, eventKey, new DoNothing()); + + var committed = await eventStreamRepository + .Commit(Id.NewId()); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].EntityPointer.Id.ShouldBe(entityPointer.Id); + committedSources[0].Messages[0].EntityPointer.Version.ShouldBe(Version.Zero); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); + committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedLease); + } + + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenExistingEventStream_WhenStagingNewEventKey_ThenCommitReturnsTrue(SourcesAdder sourcesAdder) + { + // ARRANGE + + var streamKey = new Key("StreamKey"); + var streamKeyLease = EventStreamRepository.GetStreamKeyLease(streamKey); + + var eventKey1 = new Key("EventKey1"); + var eventKeyLease1 = EventStreamRepository.GetEventKeyLease(streamKey, eventKey1); + + var eventKey2 = new Key("EventKey2"); + var eventKeyLease2 = EventStreamRepository.GetEventKeyLease(streamKey, eventKey2); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.AddEventStream(); + }); + + await using var eventStreamRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(default!, default!); + + var firstStaged = await eventStreamRepository + .Stage(streamKey, eventKey1, new DoNothing()); + + var firstCommitted = await eventStreamRepository + .Commit(Id.NewId()); + + // ARRANGE ASSERTIONS + + firstStaged.ShouldBeTrue(); + firstCommitted.ShouldBeTrue(); + + // ACT + + var secondStaged = await eventStreamRepository + .Stage(streamKey, eventKey2, new DoNothing()); + + var secondCommitted = await eventStreamRepository + .Commit(Id.NewId()); + + var entityPointerCount = await eventStreamRepository.SourceRepository + .EnumerateEntityPointers(new MatchingLeasesQuery(streamKeyLease, eventKeyLease1, eventKeyLease2)) + .CountAsync(); + + // ASSERT + + secondStaged.ShouldBeTrue(); + secondCommitted.ShouldBeTrue(); + entityPointerCount.ShouldBe(2); + } + + [Fact] + public async Task GivenExistingEventStreamMock_WhenStagingDuplicateEventKey_ThenStageReturnsFalse() + { + // ARRANGE + + var entityPointer = Id.NewId() + Version.Zero.Next(); + var streamKey = new Key("StreamKey"); + var eventKey = new Key("EventKey"); + + var sequenceMock = new MockSequence(); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + // First query checks if eventKey lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(new[] { entityPointer })); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock)); + + serviceCollection.AddEventStream(); + }); + + await using var eventStreamRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(default!, default!); + + // ACT + + var staged = await eventStreamRepository + .Stage(streamKey, eventKey, new DoNothing()); + + // ASSERT + + staged.ShouldBeFalse(); + } + + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenExistingEventStream_WhenStagingDuplicateEventKey_ThenStagedReturnsFalse( + SourcesAdder sourcesAdder) + { + // ARRANGE + + var streamKey = new Key("StreamKey"); + var eventKey = new Key("EventKey"); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.AddEventStream(); + }); + + await using var eventStreamRepository = await serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository(default!, default!); + + var stagedOnce = await eventStreamRepository + .Stage(streamKey, eventKey, new DoNothing()); + + var committedOnce = await eventStreamRepository + .Commit(Id.NewId()); + + // ARRANGE ASSERTIONS + + stagedOnce.ShouldBeTrue(); + committedOnce.ShouldBeTrue(); + + // ACT + + var stagedTwice = await eventStreamRepository + .Stage(streamKey, eventKey, new DoNothing()); + + // ASSERT + + stagedTwice.ShouldBeFalse(); + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 245453d5..c5b691d5 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -257,6 +257,12 @@ from entityAdder in AllEntityAdders() select new object[] { sourceAdder, entityAdder }; } + public static IEnumerable AddSource() + { + return from sourceAdder in AllSourceAdders + select new object[] { sourceAdder }; + } + public static IEnumerable AddEntity() { return from entityAdder in AllEntityAdders() From b176576ce534bfdb8f6ad18eb3c578b7d13a5502 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 70/93] remove redundant checks --- test/EntityDb.Common.Tests/Sources/SourceTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 522ed735..3c31f3f0 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -466,7 +466,7 @@ private static async Task BuildSource foreach (var count in counts) sourceBuilder.Append(new StoreNumber(count)); - var source = sourceBuilder.Build(sourceId).ShouldBeOfType(); + var source = sourceBuilder.Build(sourceId); if (timeStampOverride.HasValue) source = source with @@ -598,8 +598,7 @@ private async Task Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenRetur var source = sourceBuilder .Append(DeltaSeeder.Create()) - .Build(default) - .ShouldBeOfType(); + .Build(default); source = source with { From d7d65e52f886b49879edbac88438d512e5d5f5b5 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 71/93] refactor: incorporate EntitySourceBuilder into EntityRepository --- .../Entities/IEntityRepositoryFactory.cs | 59 +- .../Entities/IEntitySourceBuilder.cs | 56 - .../Entities/IEntitySourceBuilderFactory.cs | 31 - .../Entities/IMultipleEntityRepository.cs | 66 + ...pository.cs => ISingleEntityRepository.cs} | 29 +- .../Entities/ISingleEntitySourceBuilder.cs | 53 - .../IProjectionRepositoryFactory.cs | 2 +- .../Entities/EntityRepository.cs | 108 -- .../Entities/EntityRepositoryFactory.cs | 75 +- .../Entities/EntitySourceBuilder.cs | 102 -- .../Entities/EntitySourceBuilderFactory.cs | 32 - .../Entities/MultipleEntityRepository.cs | 176 +++ .../Entities/SingleEntityRepository.cs | 44 + .../Entities/SingleEntitySourceBuilder.cs | 48 - .../EntityAlreadyLoadedException.cs | 9 +- .../Extensions/ServiceCollectionExtensions.cs | 7 +- .../Extensions/SourceRepositoryExtensions.cs | 2 +- .../EntitySnapshotSourceProcessor.cs | 2 +- .../Sources/PublishSourceRepository.cs | 42 + .../Sources/SourceRepositoryWrapper.cs | 12 +- .../Sources/MongoDbSourceRepositoryFactory.cs | 8 +- .../Entities/EntityRepositoryTests.cs | 286 +++++ .../Entities/EntityTests.cs | 180 +-- .../Implementations/Seeders/DeltaSeeder.cs | 74 ++ .../Seeders/EntityRepositoryExtensions.cs | 23 + .../Projections/ProjectionsTests.cs | 31 +- .../Snapshots/SnapshotTests.cs | 20 +- .../EntitySnapshotSourceSubscriberTests.cs | 43 +- .../Sources/SingleEntitySourceBuilderTests.cs | 288 ----- .../Sources/SourceTests.cs | 1144 +++++++---------- .../TestSessionOptions.cs | 2 + test/EntityDb.Common.Tests/TestsBase.cs | 159 ++- 32 files changed, 1615 insertions(+), 1598 deletions(-) delete mode 100644 src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs delete mode 100644 src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs create mode 100644 src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs rename src/EntityDb.Abstractions/Entities/{IEntityRepository.cs => ISingleEntityRepository.cs} (58%) delete mode 100644 src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs delete mode 100644 src/EntityDb.Common/Entities/EntityRepository.cs delete mode 100644 src/EntityDb.Common/Entities/EntitySourceBuilder.cs delete mode 100644 src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs create mode 100644 src/EntityDb.Common/Entities/MultipleEntityRepository.cs create mode 100644 src/EntityDb.Common/Entities/SingleEntityRepository.cs delete mode 100644 src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs create mode 100644 src/EntityDb.Common/Sources/PublishSourceRepository.cs create mode 100644 test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs delete mode 100644 test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs index 06a29466..9e0096a6 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs @@ -1,18 +1,65 @@ +using EntityDb.Abstractions.ValueObjects; + namespace EntityDb.Abstractions.Entities; /// -/// Represents a type used to create instances of +/// Represents a type used to create instances of +/// and . /// -/// The type of entity managed by the . +/// The type of entity. public interface IEntityRepositoryFactory { /// - /// Create a new instance of + /// Create a new instance of + /// for a new entity. + /// + /// A id associated with a . + /// The name of the agent signature options. + /// The agent's use case for the source repository. + /// The agent's use case for the snapshot repository. + /// A cancellation token. + /// A new instance of . + Task> CreateSingleForNew + ( + Id entityId, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ); + + /// + /// Create a new instance of + /// for an existing entity. + /// + /// A pointer associated with a . + /// The name of the agent signature options. + /// The agent's use case for the source repository. + /// The agent's use case for the snapshot repository. + /// A cancellation token. + /// A new instance of . + Task> CreateSingleForExisting + ( + Pointer entityPointer, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ); + + /// + /// Create a new instance of /// + /// The name of the agent signature options. /// The agent's use case for the source repository. /// The agent's use case for the snapshot repository. /// A cancellation token. - /// A new instance of . - Task> CreateRepository(string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default); + /// A new instance of . + Task> CreateMultiple + ( + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ); } diff --git a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs deleted file mode 100644 index c0f03f84..00000000 --- a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilder.cs +++ /dev/null @@ -1,56 +0,0 @@ -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Entities; - -/// -/// Provides a way to construct an . Note that no operations are permanent -/// until you call and pass the result to a source repository. -/// -/// The type of the entity in the source. -public interface IEntitySourceBuilder -{ - /// - /// Returns a associated with a given id, if it is known. - /// - /// The id associated with the entity. - /// A associated with , if it is known. - TEntity GetEntity(Id entityId); - - /// - /// Indicates whether or not a associated with a given id is in memory. - /// - /// The id of the entity. - /// - /// true if a associated with is in memory, or - /// else false. - /// - bool IsEntityKnown(Id entityId); - - /// - /// Associate a with a given entity id. - /// - /// A id associated with a . - /// A . - /// The source builder. - /// - /// Call this method to load an entity that already exists before calling - /// . - /// - IEntitySourceBuilder Load(Id entityId, TEntity entity); - - /// - /// Adds a single delta to the source with a given entity id. - /// - /// The id associated with the . - /// The new delta that modifies the . - /// The source builder. - IEntitySourceBuilder Append(Id entityId, object delta); - - /// - /// Returns a new instance of . - /// - /// A new id for the new source. - /// A new instance of . - Source Build(Id sourceId); -} diff --git a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs b/src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs deleted file mode 100644 index 98d55cd6..00000000 --- a/src/EntityDb.Abstractions/Entities/IEntitySourceBuilderFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Entities; - -/// -/// Represents a type used to create instances of or -/// . -/// -/// -public interface IEntitySourceBuilderFactory -{ - /// - /// Creates a new instance of . - /// - /// The name of the agent signature options. - /// A cancellation token. - /// A new instance of . - Task> Create(string agentSignatureOptionsName, - CancellationToken cancellationToken = default); - - /// - /// Creates a new instance of . - /// - /// The name of the agent signature options. - /// The id of the entity. - /// A cancellation token. - /// A new instance of . - Task> CreateForSingleEntity(string agentSignatureOptionsName, - Id entityId, - CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs new file mode 100644 index 00000000..19366a31 --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs @@ -0,0 +1,66 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Manages the sources and snapshots of multiple entities. +/// +/// The type of the entity. +public interface IMultipleEntityRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// The backing snapshot repository (if snapshot is available). + /// + ISnapshotRepository? SnapshotRepository { get; } + + /// + /// Start a new entity if a given entity id. + /// + /// A new id for the new entity. + /// + /// Only call this method for entities that do not already exist. + /// + void Create(Id entityId); + + /// + /// Associate a with a given entity id. + /// + /// A pointer associated with a . + /// A cancellation token + /// A task. + /// + /// Call this method to load an entity that already exists before calling + /// . + /// + Task Load(Pointer entityPointer, CancellationToken cancellationToken = default); + + /// + /// Returns the snapshot of a for a given . + /// + /// The id of the entity. + /// The snapshot of a for . + TEntity Get(Id entityId); + + /// + /// Adds a single delta to the source with a given entity id. + /// + /// The id associated with the . + /// The new delta that modifies the . + void Append(Id entityId, object delta); + + /// + /// Atomically commits a source. + /// + /// A new id for the new source. + /// A cancellation token. + /// true if the commit succeeded, or false if the commit failed. + Task Commit(Id sourceId, CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs similarity index 58% rename from src/EntityDb.Abstractions/Entities/IEntityRepository.cs rename to src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs index 0e2b8d10..64eaf78b 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs @@ -6,10 +6,10 @@ namespace EntityDb.Abstractions.Entities; /// -/// Encapsulates the source repository and the snapshot repository of an entity. +/// Manages the snapshots and sources of a single entity. /// /// The type of the entity. -public interface IEntityRepository : IDisposableResource +public interface ISingleEntityRepository : IDisposableResource { /// /// The backing source repository. @@ -20,20 +20,29 @@ public interface IEntityRepository : IDisposableResource /// The backing snapshot repository (if snapshot is available). /// ISnapshotRepository? SnapshotRepository { get; } - + /// - /// Returns the snapshot of a for a given . + /// The pointer for the currently entity. /// - /// A pointer to the entity. - /// A cancellation token. - /// The snapshot of a for . - Task GetSnapshot(Pointer entityPointer, CancellationToken cancellationToken = default); + Pointer EntityPointer { get; } + + /// + /// Returns the snapshot of a . + /// + /// The snapshot of a . + TEntity Get(); + /// + /// Adds a single delta to the source. + /// + /// The new delta that modifies the . + void Append(object delta); + /// /// Atomically commits a source. /// - /// The source. + /// A new id for the new source. /// A cancellation token. /// true if the commit succeeded, or false if the commit failed. - Task Commit(Source source, CancellationToken cancellationToken = default); + Task Commit(Id sourceId, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs b/src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs deleted file mode 100644 index 9c5e00c2..00000000 --- a/src/EntityDb.Abstractions/Entities/ISingleEntitySourceBuilder.cs +++ /dev/null @@ -1,53 +0,0 @@ -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Entities; - -/// -/// A source builder for a single entity. -/// -/// The type of the entity. -public interface ISingleEntitySourceBuilder -{ - /// - /// The id used for all source builder methods, where applicable. - /// - Id EntityId { get; } - - /// - /// Returns a , if it is known. - /// - /// A , if it is known. - TEntity GetEntity(); - - /// - /// Indicates whether or not a is in memory (i.e., created or loaded). - /// - /// true if a is in memory, or else false. - bool IsEntityKnown(); - - /// - /// Associate a . - /// - /// A - /// The source builder. - /// - /// Call this method to load an entity that already exists before calling - /// . - /// - ISingleEntitySourceBuilder Load(TEntity entity); - - /// - /// Adds a single delta to the source. - /// - /// The new delta that modifies the . - /// The source builder. - ISingleEntitySourceBuilder Append(object delta); - - /// - /// Returns a new instance of . - /// - /// A new id for the new source. - /// A new instance of . - Source Build(Id sourceId); -} diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs index 2e90e03a..d09ee8fd 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs @@ -12,6 +12,6 @@ public interface IProjectionRepositoryFactory /// The agent's use case for the snapshot repository. /// A cancellation token. /// A new instance of . - Task> CreateRepository(string snapshotSessionOptionsName, + Task> CreateRepository(string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Common/Entities/EntityRepository.cs b/src/EntityDb.Common/Entities/EntityRepository.cs deleted file mode 100644 index b2ac908d..00000000 --- a/src/EntityDb.Common/Entities/EntityRepository.cs +++ /dev/null @@ -1,108 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.Common.Exceptions; -using EntityDb.Common.Sources.Queries.Standard; -using Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Common.Entities; - -internal class EntityRepository : DisposableResourceBaseClass, IEntityRepository - where TEntity : IEntity -{ - private readonly IEnumerable _sourceSubscribers; - - public EntityRepository - ( - IEnumerable sourceSubscribers, - ISourceRepository sourceRepository, - ISnapshotRepository? snapshotRepository = null - ) - { - _sourceSubscribers = sourceSubscribers; - SourceRepository = sourceRepository; - SnapshotRepository = snapshotRepository; - } - - public ISourceRepository SourceRepository { get; } - public ISnapshotRepository? SnapshotRepository { get; } - - public async Task GetSnapshot(Pointer entityPointer, CancellationToken cancellationToken = default) - { - var snapshot = SnapshotRepository is not null - ? await SnapshotRepository.GetSnapshotOrDefault(entityPointer, cancellationToken) ?? - TEntity.Construct(entityPointer.Id) - : TEntity.Construct(entityPointer.Id); - - var snapshotPointer = snapshot.GetPointer(); - - var query = new GetDeltasQuery(entityPointer, snapshotPointer.Version); - - var deltas = SourceRepository.EnumerateDeltas(query, cancellationToken); - - var entity = await deltas - .AggregateAsync - ( - snapshot, - (current, delta) => current.Reduce(delta), - cancellationToken - ); - - if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) - { - throw new SnapshotPointerDoesNotExistException(); - } - - return entity; - } - - public async Task Commit(Source source, - CancellationToken cancellationToken = default) - { - var success = await SourceRepository.Commit(source, cancellationToken); - - if (success) - { - Publish(source); - } - - return success; - } - - public override async ValueTask DisposeAsync() - { - await SourceRepository.DisposeAsync(); - - if (SnapshotRepository is not null) - { - await SnapshotRepository.DisposeAsync(); - } - } - - private void Publish(Source source) - { - foreach (var sourceSubscriber in _sourceSubscribers) - { - sourceSubscriber.Notify(source); - } - } - - public static EntityRepository Create - ( - IServiceProvider serviceProvider, - ISourceRepository sourceRepository, - ISnapshotRepository? snapshotRepository = null - ) - { - if (snapshotRepository is null) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, - sourceRepository); - } - - return ActivatorUtilities.CreateInstance>(serviceProvider, sourceRepository, - snapshotRepository); - } -} diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index 96a996b4..89b3ec16 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -1,6 +1,8 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Entities; @@ -8,37 +10,90 @@ internal class EntityRepositoryFactory : IEntityRepositoryFactory { private readonly IServiceProvider _serviceProvider; + private readonly IAgentAccessor _agentAccessor; private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory; private readonly ISourceRepositoryFactory _sourceRepositoryFactory; public EntityRepositoryFactory ( IServiceProvider serviceProvider, + IAgentAccessor agentAccessor, ISourceRepositoryFactory sourceRepositoryFactory, ISnapshotRepositoryFactory? snapshotRepositoryFactory = null ) { _serviceProvider = serviceProvider; + _agentAccessor = agentAccessor; _sourceRepositoryFactory = sourceRepositoryFactory; _snapshotRepositoryFactory = snapshotRepositoryFactory; } - public async Task> CreateRepository(string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default) + public async Task> CreateSingleForNew + ( + Id entityId, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ) + { + var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, + snapshotSessionOptionsName, cancellationToken); + + multipleEntityRepository.Create(entityId); + + return new SingleEntityRepository(multipleEntityRepository, entityId); + } + + public async Task> CreateSingleForExisting + ( + Pointer entityPointer, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ) + { + var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, + snapshotSessionOptionsName, cancellationToken); + + await multipleEntityRepository.Load(entityPointer, cancellationToken); + + return new SingleEntityRepository(multipleEntityRepository, entityPointer); + } + + public async Task> CreateMultiple + ( + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? snapshotSessionOptionsName = null, + CancellationToken cancellationToken = default + ) { - var sourceRepository = - await _sourceRepositoryFactory.CreateRepository(sourceSessionOptionsName, cancellationToken); + var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); + + var sourceRepository = await _sourceRepositoryFactory + .CreateRepository(sourceSessionOptionsName, cancellationToken); if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) { - return EntityRepository.Create(_serviceProvider, - sourceRepository); + return MultipleEntityRepository.Create + ( + _serviceProvider, + agent, + sourceRepository + ); } - var snapshotRepository = - await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); + var snapshotRepository = await _snapshotRepositoryFactory + .CreateRepository(snapshotSessionOptionsName, cancellationToken); - return EntityRepository.Create(_serviceProvider, - sourceRepository, snapshotRepository); + return MultipleEntityRepository.Create + ( + _serviceProvider, + agent, + sourceRepository, + snapshotRepository + ); } } diff --git a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs b/src/EntityDb.Common/Entities/EntitySourceBuilder.cs deleted file mode 100644 index 9534f31b..00000000 --- a/src/EntityDb.Common/Entities/EntitySourceBuilder.cs +++ /dev/null @@ -1,102 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Sources.Agents; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Exceptions; -using System.Collections.Immutable; - -namespace EntityDb.Common.Entities; - -internal sealed class EntitySourceBuilder : IEntitySourceBuilder - where TEntity : IEntity -{ - private readonly IAgent _agent; - private readonly Dictionary _knownEntities = new(); - private readonly List _messages = new(); - - public EntitySourceBuilder(IAgent agent) - { - _agent = agent; - } - - public TEntity GetEntity(Id entityId) - { - return _knownEntities[entityId]; - } - - public bool IsEntityKnown(Id entityId) - { - return _knownEntities.ContainsKey(entityId); - } - - public IEntitySourceBuilder Load(Id entityId, TEntity entity) - { - if (IsEntityKnown(entityId)) - { - throw new EntityAlreadyLoadedException(); - } - - _knownEntities.Add(entityId, entity); - - return this; - } - - public IEntitySourceBuilder Append(Id entityId, object delta) - { - ConstructIfNotKnown(entityId); - - var entity = _knownEntities[entityId].Reduce(delta); - - _messages.Add(new Message - { - Id = Id.NewId(), - EntityPointer = entity.GetPointer(), - Delta = delta, - AddLeases = delta is IAddLeasesDelta addLeasesDelta - ? addLeasesDelta.GetLeases(entity).ToImmutableArray() - : ImmutableArray.Empty, - AddTags = delta is IAddTagsDelta addTagsDelta - ? addTagsDelta.GetTags(entity).ToImmutableArray() - : ImmutableArray.Empty, - DeleteLeases = delta is IDeleteLeasesDelta deleteLeasesDelta - ? deleteLeasesDelta.GetLeases(entity).ToImmutableArray() - : ImmutableArray.Empty, - DeleteTags = delta is IDeleteTagsDelta deleteTagsDelta - ? deleteTagsDelta.GetTags(entity).ToImmutableArray() - : ImmutableArray.Empty, - }); - - _knownEntities[entityId] = entity; - - return this; - } - - public Source Build(Id sourceId) - { - var source = new Source - { - Id = sourceId, - TimeStamp = _agent.TimeStamp, - AgentSignature = _agent.Signature, - Messages = _messages.ToImmutableArray(), - }; - - _messages.Clear(); - - return source; - } - - private void ConstructIfNotKnown(Id entityId) - { - if (IsEntityKnown(entityId)) - { - return; - } - - var entity = TEntity.Construct(entityId); - - _knownEntities.Add(entityId, entity); - } -} diff --git a/src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs b/src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs deleted file mode 100644 index 2501bd75..00000000 --- a/src/EntityDb.Common/Entities/EntitySourceBuilderFactory.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Sources.Agents; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Entities; - -internal sealed class EntitySourceBuilderFactory : IEntitySourceBuilderFactory - where TEntity : IEntity -{ - private readonly IAgentAccessor _agentAccessor; - - public EntitySourceBuilderFactory(IAgentAccessor agentAccessor) - { - _agentAccessor = agentAccessor; - } - - public async Task> Create(string agentSignatureOptionsName, - CancellationToken cancellationToken) - { - var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); - - return new EntitySourceBuilder(agent); - } - - public async Task> CreateForSingleEntity(string agentSignatureOptionsName, - Id entityId, CancellationToken cancellationToken) - { - var sourceBuilder = await Create(agentSignatureOptionsName, cancellationToken); - - return new SingleEntitySourceBuilder(sourceBuilder, entityId); - } -} diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs new file mode 100644 index 00000000..90711e6d --- /dev/null +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -0,0 +1,176 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Entities.Deltas; +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Queries.Standard; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Immutable; + +namespace EntityDb.Common.Entities; + +internal class MultipleEntityRepository : DisposableResourceBaseClass, IMultipleEntityRepository + where TEntity : IEntity +{ + private readonly IAgent _agent; + private readonly Dictionary _knownEntities = new(); + private readonly List _messages = new(); + + public MultipleEntityRepository + ( + IAgent agent, + ISourceRepository sourceRepository, + ISnapshotRepository? snapshotRepository = null + ) + { + _agent = agent; + SourceRepository = sourceRepository; + SnapshotRepository = snapshotRepository; + } + + public ISourceRepository SourceRepository { get; } + public ISnapshotRepository? SnapshotRepository { get; } + + public void Create(Id entityId) + { + if (_knownEntities.ContainsKey(entityId)) + { + throw new EntityAlreadyLoadedException(); + } + + var entity = TEntity.Construct(entityId); + + _knownEntities.Add(entityId, entity); + } + + public async Task Load(Pointer entityPointer, CancellationToken cancellationToken = default) + { + if (_knownEntities.ContainsKey(entityPointer.Id)) + { + throw new EntityAlreadyLoadedException(); + } + + var snapshot = SnapshotRepository is not null + ? await SnapshotRepository.GetSnapshotOrDefault(entityPointer, cancellationToken) ?? + TEntity.Construct(entityPointer.Id) + : TEntity.Construct(entityPointer.Id); + + var snapshotPointer = snapshot.GetPointer(); + + var query = new GetDeltasQuery(entityPointer, snapshotPointer.Version); + + var entity = await SourceRepository + .EnumerateDeltas(query, cancellationToken) + .AggregateAsync + ( + snapshot, + (current, delta) => current.Reduce(delta), + cancellationToken + ); + + if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) + { + throw new SnapshotPointerDoesNotExistException(); + } + + _knownEntities.Add(entityPointer.Id, entity); + } + + public TEntity Get(Id entityId) + { + if (!_knownEntities.TryGetValue(entityId, out var entity)) + { + throw new EntityNotLoadedException(); + } + + return entity; + } + + public void Append(Id entityId, object delta) + { + if (!_knownEntities.TryGetValue(entityId, out var entity)) + { + throw new EntityNotLoadedException(); + } + + entity = entity.Reduce(delta); + + _messages.Add(new Message + { + Id = Id.NewId(), + EntityPointer = entity.GetPointer(), + Delta = delta, + AddLeases = delta is IAddLeasesDelta addLeasesDelta + ? addLeasesDelta.GetLeases(entity).ToImmutableArray() + : ImmutableArray.Empty, + AddTags = delta is IAddTagsDelta addTagsDelta + ? addTagsDelta.GetTags(entity).ToImmutableArray() + : ImmutableArray.Empty, + DeleteLeases = delta is IDeleteLeasesDelta deleteLeasesDelta + ? deleteLeasesDelta.GetLeases(entity).ToImmutableArray() + : ImmutableArray.Empty, + DeleteTags = delta is IDeleteTagsDelta deleteTagsDelta + ? deleteTagsDelta.GetTags(entity).ToImmutableArray() + : ImmutableArray.Empty, + }); + + _knownEntities[entityId] = entity; + } + + public async Task Commit(Id sourceId, + CancellationToken cancellationToken = default) + { + var source = new Source + { + Id = sourceId, + TimeStamp = _agent.TimeStamp, + AgentSignature = _agent.Signature, + Messages = _messages.ToImmutableArray(), + }; + + _messages.Clear(); + + return await SourceRepository.Commit(source, cancellationToken); + } + + public override async ValueTask DisposeAsync() + { + await SourceRepository.DisposeAsync(); + + if (SnapshotRepository is not null) + { + await SnapshotRepository.DisposeAsync(); + } + } + + public static MultipleEntityRepository Create + ( + IServiceProvider serviceProvider, + IAgent agent, + ISourceRepository sourceRepository, + ISnapshotRepository? snapshotRepository = null + ) + { + if (snapshotRepository is null) + { + return ActivatorUtilities.CreateInstance> + ( + serviceProvider, + agent, + sourceRepository + ); + } + + return ActivatorUtilities.CreateInstance> + ( + serviceProvider, + agent, + sourceRepository, + snapshotRepository + ); + } +} diff --git a/src/EntityDb.Common/Entities/SingleEntityRepository.cs b/src/EntityDb.Common/Entities/SingleEntityRepository.cs new file mode 100644 index 00000000..89d5f60d --- /dev/null +++ b/src/EntityDb.Common/Entities/SingleEntityRepository.cs @@ -0,0 +1,44 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.Entities; + +internal sealed class SingleEntityRepository : DisposableResourceBaseClass, ISingleEntityRepository + where TEntity : IEntity +{ + private readonly IMultipleEntityRepository _multipleEntityRepository; + + public SingleEntityRepository(IMultipleEntityRepository multipleEntityRepository, Pointer entityPointer) + { + _multipleEntityRepository = multipleEntityRepository; + + EntityPointer = entityPointer; + } + + public ISourceRepository SourceRepository => _multipleEntityRepository.SourceRepository; + public ISnapshotRepository? SnapshotRepository => _multipleEntityRepository.SnapshotRepository; + public Pointer EntityPointer { get; } + + public TEntity Get() + { + return _multipleEntityRepository.Get(EntityPointer.Id); + } + + public void Append(object delta) + { + _multipleEntityRepository.Append(EntityPointer.Id, delta); + } + + public Task Commit(Id sourceId, CancellationToken cancellationToken = default) + { + return _multipleEntityRepository.Commit(sourceId, cancellationToken); + } + + public override ValueTask DisposeAsync() + { + return _multipleEntityRepository.DisposeAsync(); + } +} diff --git a/src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs b/src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs deleted file mode 100644 index 1ee59eb7..00000000 --- a/src/EntityDb.Common/Entities/SingleEntitySourceBuilder.cs +++ /dev/null @@ -1,48 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Entities; - -internal sealed class SingleEntitySourceBuilder : ISingleEntitySourceBuilder - where TEntity : IEntity -{ - private readonly IEntitySourceBuilder _sourceBuilder; - - internal SingleEntitySourceBuilder(IEntitySourceBuilder sourceBuilder, Id entityId) - { - _sourceBuilder = sourceBuilder; - EntityId = entityId; - } - - public Id EntityId { get; } - - public TEntity GetEntity() - { - return _sourceBuilder.GetEntity(EntityId); - } - - public bool IsEntityKnown() - { - return _sourceBuilder.IsEntityKnown(EntityId); - } - - public ISingleEntitySourceBuilder Load(TEntity entity) - { - _sourceBuilder.Load(EntityId, entity); - - return this; - } - - public ISingleEntitySourceBuilder Append(object delta) - { - _sourceBuilder.Append(EntityId, delta); - - return this; - } - - public Source Build(Id sourceId) - { - return _sourceBuilder.Build(sourceId); - } -} diff --git a/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs b/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs index 32ec6f48..34673f9a 100644 --- a/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs +++ b/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs @@ -2,11 +2,10 @@ namespace EntityDb.Common.Exceptions; -/// -/// The exception that is thrown when an actor passes an entity id to -/// -/// with an entity id that has already been loaded. -/// public sealed class EntityAlreadyLoadedException : Exception { } + +public sealed class EntityNotLoadedException : Exception +{ +} diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 34617f14..a41277a1 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -145,17 +145,14 @@ public static void AddAgentAccessor(this IServiceCollection serv } /// - /// Adds a transient and a transient implementation of - /// to a service collection. + /// Adds a transient to a + /// service collection. /// /// The service collection. /// The type of the entity. public static void AddEntity(this IServiceCollection serviceCollection) where TEntity : IEntity { - serviceCollection - .AddTransient, EntitySourceBuilderFactory>(); - serviceCollection.AddTransient, EntityRepositoryFactory>(); } diff --git a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs index 7e3e9504..0c7ccdb2 100644 --- a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs @@ -61,7 +61,7 @@ public static async Task GetSource ( this ISourceRepository sourceRepository, Id sourceId, - CancellationToken cancellationToken + CancellationToken cancellationToken = default ) { var query = new GetSourceQuery(sourceId); diff --git a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs index 2b21eab3..1418ccde 100644 --- a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs @@ -42,7 +42,7 @@ public async Task Process(Source source, CancellationToken cancellationToken) } await using var entityRepository = await _entityRepositoryFactory - .CreateRepository(_sourceSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); + .CreateMultiple(default!, _sourceSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); if (entityRepository.SnapshotRepository is null) { diff --git a/src/EntityDb.Common/Sources/PublishSourceRepository.cs b/src/EntityDb.Common/Sources/PublishSourceRepository.cs new file mode 100644 index 00000000..19bf12f8 --- /dev/null +++ b/src/EntityDb.Common/Sources/PublishSourceRepository.cs @@ -0,0 +1,42 @@ +using EntityDb.Abstractions.Sources; +using Microsoft.Extensions.DependencyInjection; + +namespace EntityDb.Common.Sources; + +internal sealed class PublishSourceRepository : SourceRepositoryWrapper +{ + private readonly IEnumerable _sourceSubscribers; + + public PublishSourceRepository + ( + ISourceRepository sourceRepository, + IEnumerable sourceSubscribers + ) : base(sourceRepository) + { + _sourceSubscribers = sourceSubscribers; + } + + public override async Task Commit(Source source, CancellationToken cancellationToken = default) + { + var committed = await base.Commit(source, cancellationToken); + + if (!committed) + { + return false; + } + + foreach (var sourceSubscriber in _sourceSubscribers) + { + sourceSubscriber.Notify(source); + } + + return true; + } + + public static ISourceRepository Create(IServiceProvider serviceProvider, + ISourceRepository sourceRepository) + { + return ActivatorUtilities.CreateInstance(serviceProvider, + sourceRepository); + } +} diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs index 91759632..f62f611f 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -102,7 +102,7 @@ public IAsyncEnumerable> EnumerateAnnotatedDeltas(I return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageQuery, cancellationToken)); } - public Task Commit(Source source, + public virtual Task Commit(Source source, CancellationToken cancellationToken = default) { return WrapCommand(() => _sourceRepository.Commit(source, cancellationToken)); @@ -113,7 +113,13 @@ public override async ValueTask DisposeAsync() await _sourceRepository.DisposeAsync(); } - protected abstract IAsyncEnumerable WrapQuery(Func> enumerable); + protected virtual IAsyncEnumerable WrapQuery(Func> enumerable) + { + return enumerable.Invoke(); + } - protected abstract Task WrapCommand(Func> task); + protected virtual Task WrapCommand(Func> task) + { + return task.Invoke(); + } } diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs index 9e7f7785..55d8e615 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs @@ -57,12 +57,16 @@ public ISourceRepository CreateRepository IMongoSession mongoSession ) { - var mongoDbSourceRepository = new MongoDbSourceRepository + ISourceRepository sourceRepository = new MongoDbSourceRepository ( mongoSession, _envelopeService ); - return TryCatchSourceRepository.Create(_serviceProvider, mongoDbSourceRepository); + sourceRepository = TryCatchSourceRepository.Create(_serviceProvider, sourceRepository); + + sourceRepository = PublishSourceRepository.Create(_serviceProvider, sourceRepository); + + return sourceRepository; } } diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs new file mode 100644 index 00000000..a15aa262 --- /dev/null +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -0,0 +1,286 @@ +using System.Diagnostics.CodeAnalysis; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Deltas; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.Entities; + +[Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class EntityRepositoryTests : TestsBase +{ + public EntityRepositoryTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base( + serviceProvider, databaseContainerFixture) + { + } + + private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var entityRepository = await GetReadOnlyEntityRepository(serviceScope, false); + + // ASSERT + + Should.Throw(() => entityRepository.Get(default)); + } + + private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + var expectedEntityId = Id.NewId(); + + var expectedEntity = TEntity + .Construct(expectedEntityId); + + var entityRepository = await GetWriteEntityRepository(serviceScope, false); + + entityRepository.Create(expectedEntityId); + + // ARRANGE ASSERTIONS + + Should.NotThrow(() => entityRepository.Get(expectedEntityId)); + + // ACT + + var actualEntity = entityRepository.Get(expectedEntityId); + var actualEntityId = actualEntity.GetPointer().Id; + + // ASSERT + + actualEntity.ShouldBeEquivalentTo(expectedEntity); + actualEntityId.ShouldBe(expectedEntityId); + } + + private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + var committedSources = new List(); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); + + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + + // ACT + + entityRepository.Create(default); + + entityRepository.Append(default, new AddLease(new Lease(default!, default!, default!))); + + await entityRepository.Commit(default); + + // ASSERT + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); + } + + private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + var entityId = Id.NewId(); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + + writeRepository.Create(entityId); + writeRepository.Append(entityId, new DoNothing()); + + var committed = await writeRepository.Commit(default); + + // ARRANGE ASSERTIONS + + committed.ShouldBeTrue(); + + // ACT + + await using var readRepository = await GetReadOnlyEntityRepository(serviceScope, false); + + await readRepository.Load(entityId); + + // ASSERT + + await Should.ThrowAsync(readRepository.Load(entityId)); + } + + private async Task Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + var committedSources = new List(); + + var numberOfVersionsToTest = 10; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); + + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + + entityRepository.Create(default); + + // ACT + + for (var i = 1; i <= numberOfVersionsToTest; i++) + { + entityRepository.Append(default, new DoNothing()); + } + + await entityRepository.Commit(default); + + // ASSERT + + committedSources.Count.ShouldBe(1); + + var expectedVersion = Version.Zero; + + for (var i = 1; i <= numberOfVersionsToTest; i++) + { + expectedVersion = expectedVersion.Next(); + + committedSources[0].Messages[i - 1].EntityPointer.Version.ShouldBe(expectedVersion); + } + } + + private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : IEntity + { + // ARRANGE + + var committedSources = new List(); + + var expectedDelta = new DoNothing(); + + using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); + + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); + }); + + // ACT + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + + entityRepository.Create(default); + entityRepository.Append(default, expectedDelta); + + await entityRepository.Commit(default); + + // ASSERT + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].Delta.ShouldBeEquivalentTo(new DoNothing()); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenEntityNotKnown_WhenGettingEntity_ThenThrow(SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases(SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements(SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(SourcesAdder sourcesAdder, EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 7bc5e4f5..ff5e791d 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; @@ -6,7 +7,9 @@ using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; using EntityDb.Common.Polyfills; +using EntityDb.Common.Sources.Agents; using EntityDb.Common.Tests.Implementations.Deltas; +using EntityDb.Common.Tests.Implementations.Seeders; using EntityDb.Common.Tests.Implementations.Snapshots; using Microsoft.Extensions.DependencyInjection; using Moq; @@ -25,34 +28,6 @@ public EntityTests(IServiceProvider serviceProvider, DatabaseContainerFixture da { } - private static async Task BuildSource - ( - IServiceScope serviceScope, - Id entityId, - Version from, - Version to, - TEntity? entity = default - ) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - if (entity is not null) sourceBuilder.Load(entity); - - for (var i = from; i.Value <= to.Value; i = i.Next()) - { - if (sourceBuilder.IsEntityKnown() && - sourceBuilder.GetEntity().Pointer.Version.Value >= i.Value) continue; - - sourceBuilder.Append(new DoNothing()); - } - - return sourceBuilder.Build(Id.NewId()); - } - - private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic @@ -62,8 +37,6 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe const ulong n = 10UL; const ulong m = 5UL; - var versionN = new Version(n); - var versionM = new Version(m); using var serviceScope = CreateServiceScope(serviceCollection => @@ -74,26 +47,29 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe var entityId = Id.NewId(); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write, - TestSessionOptions.Write); - - var source = await BuildSource(serviceScope, entityId, new Version(1), versionN); - - var sourceCommitted = await entityRepository.Commit(source); - + await using var writeRepository = await GetWriteEntityRepository(serviceScope, true); + + writeRepository.Create(entityId); + writeRepository.Seed(entityId, m); + writeRepository.Seed(entityId, n - m); + + var committed = await writeRepository.Commit(default); + // ARRANGE ASSERTIONS - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); // ACT - var entityAtVersionM = await entityRepository.GetSnapshot(entityId + versionM); + await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, true); + await readOnlyRepository.Load(entityId + new Version(m)); + + var entity = readOnlyRepository.Get(entityId); + // ASSERT - entityAtVersionM.Pointer.Version.ShouldBe(versionM); + entity.Pointer.Version.ShouldBe(versionM); } private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetDeltasRuns( @@ -102,7 +78,8 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T { // ARRANGE - var expectedVersion = new Version(10); + const uint expectedVersionNumber = 10; + var expectedVersion = new Version(expectedVersionNumber); var entityId = Id.NewId(); @@ -143,26 +120,28 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T serviceCollection.AddSingleton(sourceRepositoryFactoryMock.Object); }); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); - var source = - await BuildSource(serviceScope, entityId, new Version(1), expectedVersion); - - var sourceCommitted = await entityRepository.Commit(source); + writeRepository.Create(entityId); + writeRepository.Seed(entityId, expectedVersionNumber); + + var committed = await writeRepository.Commit(default); // ARRANGE ASSERTIONS - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); // ACT - var currenEntity = await entityRepository.GetSnapshot(entityId); + await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, false); + + await readOnlyRepository.Load(entityId); + + var entity = readOnlyRepository.Get(entityId); // ASSERT - currenEntity.Pointer.Version.ShouldBe(expectedVersion); + entity.Pointer.Version.ShouldBe(expectedVersion); sourceRepositoryMock .Verify( @@ -189,9 +168,7 @@ private async Task await using var snapshotRepositoryFactory = serviceScope.ServiceProvider .GetService>(); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository("NOT NULL", "NOT NULL"); + await using var entityRepository = await GetReadOnlyEntityRepository(serviceScope, true); // ASSERT @@ -220,9 +197,7 @@ private async Task await using var snapshotRepositoryFactory = serviceScope.ServiceProvider .GetService>(); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository("NOT NULL"); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); // ASSERT @@ -251,9 +226,7 @@ private async Task await using var snapshotRepositoryFactory = serviceScope.ServiceProvider .GetService>(); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository("NOT NULL", "NOT NULL"); + await using var entityRepository = await GetReadOnlyEntityRepository(serviceScope, true); // ASSERT @@ -287,17 +260,16 @@ private async Task // ACT - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository("NOT NULL", "NOT NULL"); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); - var snapshotOrDefault = await entityRepository.GetSnapshot(default); + await entityRepository.Load(default); + + var entity = entityRepository.Get(default); // ASSERT - snapshotOrDefault.ShouldNotBe(default); - snapshotOrDefault.ShouldNotBe(snapshot); - snapshotOrDefault.Pointer.Version.ShouldBe( + entity.ShouldNotBe(snapshot); + entity.Pointer.Version.ShouldBe( new Version(snapshot.Pointer.Version.Value + Convert.ToUInt64(newDeltas.Length))); } @@ -314,16 +286,62 @@ private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_The serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); }); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(default!); + await using var entityRepository = await GetReadOnlyEntityRepository(serviceScope, false); // ASSERT - await Should.ThrowAsync(async () => + Should.Throw(() => entityRepository.Get(default)); + } + + private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity( + SourcesAdder sourcesAdder, EntityAdder entityAdder) + where TEntity : class, IEntity, ISnapshotWithTestLogic + { + // ARRANGE + + using var serviceScope = CreateServiceScope(serviceCollection => { - await entityRepository.GetSnapshot(default); + sourcesAdder.AddDependencies.Invoke(serviceCollection); + entityAdder.AddDependencies.Invoke(serviceCollection); }); + + var entityId = Id.NewId(); + + var expectedEntity = TEntity.Construct(entityId).WithVersion(new Version(1)); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + + var source = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = Id.NewId(), + EntityPointer = entityId, + Delta = new DoNothing(), + } + }.ToImmutableArray() + }; + + var committed = await entityRepository.SourceRepository.Commit(source); + + // ARRANGE ASSERTIONS + + committed.ShouldBeTrue(); + + // ACT + + await entityRepository.Load(entityId); + + var actualEntity = entityRepository.Get(entityId); + + // ASSERT + + actualEntity.ShouldBeEquivalentTo(expectedEntity); } [Theory] @@ -408,4 +426,16 @@ public Task GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow(EntityAd new object?[] { entityAdder } ); } + + [Theory] + [MemberData(nameof(AddSourcesAndEntity))] + public Task GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity(SourcesAdder sourcesAdder, + EntityAdder entityAdder) + { + return RunGenericTestAsync + ( + new[] { entityAdder.EntityType }, + new object?[] { sourcesAdder, entityAdder } + ); + } } \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs index 69eb78dd..dc2de17e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs @@ -1,7 +1,81 @@ +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Common.Tests.Implementations.Deltas; namespace EntityDb.Common.Tests.Implementations.Seeders; +public interface IDeltaSeeder +{ + object Create(ulong versionNumber); +} + +public class AddTagSeeder : IDeltaSeeder +{ + private readonly ITag _tag; + + public AddTagSeeder(ITag tag) + { + _tag = tag; + } + + public object Create(ulong versionNumber) + { + return new AddTag(_tag); + } +} + +public class DeleteTagSeeder : IDeltaSeeder +{ + private readonly ITag _tag; + + public DeleteTagSeeder(ITag tag) + { + _tag = tag; + } + + public object Create(ulong versionNumber) + { + return new DeleteTag(_tag); + } +} + +public class AddLeaseSeeder : IDeltaSeeder +{ + private readonly ILease _lease; + + public AddLeaseSeeder(ILease lease) + { + _lease = lease; + } + + public object Create(ulong versionNumber) + { + return new AddLease(_lease); + } +} + +public class DeleteLeaseSeeder : IDeltaSeeder +{ + private readonly ILease _lease; + + public DeleteLeaseSeeder(ILease lease) + { + _lease = lease; + } + + public object Create(ulong versionNumber) + { + return new DeleteLease(_lease); + } +} + +public class StoreNumberSeeder : IDeltaSeeder +{ + public object Create(ulong versionNumber) + { + return new StoreNumber(versionNumber); + } +} + public static class DeltaSeeder { public static object Create() diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs new file mode 100644 index 00000000..261c5d43 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs @@ -0,0 +1,23 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Tests.Implementations.Deltas; +using EntityDb.Common.Tests.Implementations.Snapshots; + +namespace EntityDb.Common.Tests.Implementations.Seeders; + +public static class EntityRepositoryExtensions +{ + public static void Seed + ( + this IMultipleEntityRepository entityRepository, + Id entityId, + ulong numDeltas + ) + where TEntity : class, IEntity, ISnapshotWithTestLogic + { + for (ulong i = 0; i < numDeltas; i++) + { + entityRepository.Append(entityId, new DoNothing()); + } + } +} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index 6269573f..d50c168c 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -5,7 +5,6 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests.Implementations.Seeders; using EntityDb.Common.Tests.Implementations.Snapshots; -using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; using Version = EntityDb.Abstractions.ValueObjects.Version; @@ -34,9 +33,7 @@ private async Task Generic_GivenEmptySourceRepository_WhenGettingProjection_Then projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); }); - await using var projectionRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + await using var projectionRepository = await GetReadOnlyProjectionRepository(serviceScope, true); // ACT & ASSERT @@ -44,7 +41,6 @@ await Should.ThrowAsync(() => projectionRepository.GetSnapshot(default)); } - private async Task Generic_GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, @@ -61,9 +57,6 @@ private async Task projection.Pointer.Version == new Version(replaceAtVersionValue); var entityId = Id.NewId(); - var firstSource = SourceSeeder.Create(entityId, replaceAtVersionValue); - var secondSource = SourceSeeder.Create(entityId, - numberOfVersions - replaceAtVersionValue, replaceAtVersionValue); using var serviceScope = CreateServiceScope(serviceCollection => { @@ -72,17 +65,19 @@ private async Task projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); }); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); - - await using var projectionRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + await using var projectionRepository = await GetReadOnlyProjectionRepository(serviceScope, true); - var firstSourceCommitted = await entityRepository.Commit(firstSource); - var secondSourceCommitted = await entityRepository.Commit(secondSource); + entityRepository.Create(entityId); + + entityRepository.Seed(entityId, replaceAtVersionValue); + + var firstSourceCommitted = await entityRepository.Commit(Id.NewId()); + + entityRepository.Seed(entityId, numberOfVersions - replaceAtVersionValue); + var secondSourceCommitted = await entityRepository.Commit(Id.NewId()); + // ARRANGE ASSERTIONS numberOfVersions.ShouldBeGreaterThan(replaceAtVersionValue); @@ -100,9 +95,7 @@ private async Task // ASSERT currentProjection.Pointer.Version.Value.ShouldBe(numberOfVersions); - projectionSnapshot.ShouldNotBeNull(); - projectionSnapshot.Pointer.Version.Value.ShouldBe(replaceAtVersionValue); } diff --git a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs index 1fab1edb..e92c5f5f 100644 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs @@ -89,9 +89,7 @@ private async Task var snapshot = TSnapshot.Construct(default).WithVersion(new Version(300)); - await using var snapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnly); + await using var snapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); // ACT @@ -136,9 +134,7 @@ private async Task TSnapshot.ShouldRecordAsLatestLogic.Value = (_, _) => true; }); - await using var writeSnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + await using var writeSnapshotRepository = await GetWriteSnapshotRepository(serviceScope); var inserted = await writeSnapshotRepository.PutSnapshot(latestPointer, snapshot); @@ -186,17 +182,11 @@ private async Task Generic_GivenCommittedSnapshot_WhenReadInVariousReadModes_The snapshotAdder.AddDependencies.Invoke(serviceCollection); }); - await using var writeSnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + await using var writeSnapshotRepository = await GetWriteSnapshotRepository(serviceScope); - await using var readOnlySnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnly); + await using var readOnlySnapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); - await using var readOnlySecondaryPreferredSnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnlySecondaryPreferred); + await using var readOnlySecondaryPreferredSnapshotRepository = await GetReadOnlySnapshotRepository(serviceScope, secondaryPreferred: true); var inserted = await writeSnapshotRepository.PutSnapshot(entityId, expectedSnapshot); diff --git a/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs index 205484b8..e0cbbef9 100644 --- a/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs +++ b/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs @@ -22,7 +22,7 @@ public EntitySnapshotSourceSubscriberTests(IServiceProvider startupServiceProvid private async Task Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotSourceSubscriber_ThenAlwaysWriteSnapshot< TEntity>( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) + SourcesAdder sourcesAdder, SnapshotAdder snapshotAdder) where TEntity : class, IEntity, ISnapshotWithTestLogic { // ARRANGE @@ -32,28 +32,27 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); + snapshotAdder.AddDependencies.Invoke(serviceCollection); }); var entityId = Id.NewId(); const uint numberOfVersions = 10; - var source = SourceSeeder.Create(entityId, numberOfVersions); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); - - await using var snapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnly); + await using var snapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); // ACT + + entityRepository.Create(entityId); + entityRepository.Seed(entityId, numberOfVersions); + + var committed = await entityRepository + .Commit(default); - var committed = await entityRepository.Commit(source); - - var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); + var snapshot = await snapshotRepository + .GetSnapshotOrDefault(entityId); // ASSERT @@ -89,22 +88,18 @@ private async Task sourcesAdder.AddDependencies.Invoke(serviceCollection); snapshotAdder.AddDependencies.Invoke(serviceCollection); }); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + await using var snapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); var entityId = Id.NewId(); - var source = SourceSeeder.Create(entityId, 10); - - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); - - await using var snapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnly); - // ACT - await entityRepository.Commit(source); + entityRepository.Create(entityId); + entityRepository.Seed(entityId, 10); + + await entityRepository.Commit(default); var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); diff --git a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs b/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs deleted file mode 100644 index c38fe736..00000000 --- a/test/EntityDb.Common.Tests/Sources/SingleEntitySourceBuilderTests.cs +++ /dev/null @@ -1,288 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Exceptions; -using EntityDb.Common.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Deltas; -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.Common.Tests.Sources; - -[SuppressMessage("ReSharper", "UnusedMember.Local")] -public class SingleEntitySourceBuilderTests : TestsBase -{ - public SingleEntitySourceBuilderTests(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - - private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow(EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - // ASSERT - - sourceBuilder.IsEntityKnown().ShouldBeFalse(); - - Should.Throw(() => sourceBuilder.GetEntity()); - } - - private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var expectedEntityId = Id.NewId(); - - var expectedEntity = TEntity - .Construct(expectedEntityId); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, expectedEntityId); - - sourceBuilder.Load(expectedEntity); - - // ARRANGE ASSERTIONS - - sourceBuilder.IsEntityKnown().ShouldBeTrue(); - - // ACT - - var actualEntityId = sourceBuilder.EntityId; - var actualEntity = sourceBuilder.GetEntity(); - - // ASSERT - - actualEntityId.ShouldBe(expectedEntityId); - actualEntity.ShouldBe(expectedEntity); - } - - private async Task - Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - // ACT - - var source = sourceBuilder - .Append(new AddLease(new Lease(default!, default!, default!))) - .Build(default); - - // ASSERT - - source.Messages.Length.ShouldBe(1); - source.Messages[0].AddLeases.Length.ShouldBe(1); - } - - private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var entityId = Id.NewId(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddScoped(_ => - GetMockedSourceRepositoryFactory( - new object[] { new DoNothing() })); - }); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(default!); - - var entity = await entityRepository.GetSnapshot(entityId); - - // ACT - - sourceBuilder.Load(entity); - - // ASSERT - - Should.Throw(() => { sourceBuilder.Load(entity); }); - } - - private async Task - Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var numberOfVersionsToTest = new Version(10); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddScoped(_ => - GetMockedSourceRepositoryFactory()); - }); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - // ACT - - for (var i = new Version(1); i.Value <= numberOfVersionsToTest.Value; i = i.Next()) - sourceBuilder.Append(new DoNothing()); - - var source = sourceBuilder.Build(default); - - // ASSERT - - for (var v = new Version(1); v.Value <= numberOfVersionsToTest.Value; v = v.Next()) - { - var index = (int)(v.Value - 1); - - source.Messages[index].EntityPointer.Version.ShouldBe(v); - } - } - - private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var entityId = Id.NewId(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddScoped(_ => - GetMockedSourceRepositoryFactory(new object[] { new DoNothing() })); - }); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(default!); - - var entity = await entityRepository.GetSnapshot(entityId); - - // ACT - - var source = sourceBuilder - .Load(entity) - .Append(new DoNothing()) - .Build(default); - - // ASSERT - - source.Messages.Length.ShouldBe(1); - - source.Messages[0].Delta.ShouldBeEquivalentTo(new DoNothing()); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenEntityNotKnown_WhenGettingEntity_ThenThrow(EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 3c31f3f0..98a1c4dc 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -1,12 +1,9 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; -using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Agents; using EntityDb.Common.Sources.Attributes; using EntityDb.Common.Sources.Queries; @@ -16,13 +13,11 @@ using EntityDb.Common.Tests.Implementations.Leases; using EntityDb.Common.Tests.Implementations.Queries; using EntityDb.Common.Tests.Implementations.Seeders; -using EntityDb.Common.Tests.Implementations.Snapshots; using EntityDb.Common.Tests.Implementations.Tags; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Moq; using Shouldly; using Xunit; using Version = EntityDb.Abstractions.ValueObjects.Version; @@ -37,15 +32,14 @@ public SourceTests(IServiceProvider startupServiceProvider, DatabaseContainerFix : base(startupServiceProvider, databaseContainerFixture) { } - + private static async Task PutSources ( IServiceScope serviceScope, List sources ) { - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); foreach (var source in sources) { @@ -449,38 +443,34 @@ ITag[] GetExpectedResults(bool invert) } } - private static async Task BuildSource + private static Source CreateSource ( - IServiceScope serviceScope, - Id sourceId, - Id entityId, - IEnumerable counts, - TimeStamp? timeStampOverride = null, - object? agentSignatureOverride = null + IEnumerable versionNumbers, + Id? id = null, + Id? entityId = null, + TimeStamp? timeStamp = null, + object? agentSignature = null, + IDeltaSeeder? deltaSeeder = null ) - where TEntity : IEntity { - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - foreach (var count in counts) sourceBuilder.Append(new StoreNumber(count)); - - var source = sourceBuilder.Build(sourceId); - - if (timeStampOverride.HasValue) - source = source with - { - TimeStamp = timeStampOverride.Value, - }; - - if (agentSignatureOverride is not null) - source = source with - { - AgentSignature = agentSignatureOverride, - }; + var nonNullableEntityId = entityId ?? Id.NewId(); - return source; + deltaSeeder ??= new StoreNumberSeeder(); + + return new Source + { + Id = id ?? Id.NewId(), + TimeStamp = timeStamp ?? TimeStamp.UtcNow, + AgentSignature = agentSignature ?? new UnknownAgentSignature(new Dictionary()), + Messages = versionNumbers + .Select(versionNumber =>new Message + { + Id = Id.NewId(), + EntityPointer = nonNullableEntityId + new Version(versionNumber), + Delta = deltaSeeder.Create(versionNumber), + }) + .ToImmutableArray(), + }; } private static Id[] GetSortedIds(int numberOfIds) @@ -492,36 +482,97 @@ private static Id[] GetSortedIds(int numberOfIds) .ToArray(); } - private async Task - Generic_GivenReadOnlyMode_WhenCommittingSource_ThenCannotWriteInReadOnlyModeExceptionIsLogged( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) + where TOptions : class + { + const ulong countTo = 20UL; + const ulong gte = 5UL; + const ulong lte = 15UL; + + using var serviceScope = CreateServiceScope(serviceCollection => + { + sourcesAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + var sourceIds = GetSortedIds((int)countTo); + var entityIds = GetSortedIds((int)countTo); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + var deltas = new object[] { new DoNothing() }; + + for (var i = 1UL; i <= countTo; i++) + { + var currentSourceId = sourceIds[i - 1]; + var currentEntityId = entityIds[i - 1]; + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + expectedObjects.Add(i is >= gte and <= lte, currentSourceId, currentEntityId, agentSignature, deltas, + leases, tags); + + var source = new Source + { + Id = currentSourceId, + TimeStamp = TimeStamp.UtcNow, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta =>new Message + { + Id = Id.NewId(), + EntityPointer = currentEntityId, + Delta = delta, + AddLeases = leases.ToImmutableArray(), + AddTags = tags.ToImmutableArray(), + }) + .ToImmutableArray(), + }; + + sources.Add(source); + } + + var options = serviceScope.ServiceProvider + .GetRequiredService>() + .Create("Count"); + + var query = new CountQuery(gte, lte, options); + + await PutSources(serviceScope, sources); + await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenReadOnlyMode_WhenCommittingSource_ThenCannotWriteInReadOnlyModeExceptionIsLogged(SourcesAdder sourcesAdder) { // ARRANGE - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + //var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + var logs = new List(); + using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.RemoveAll(typeof(ILoggerFactory)); - serviceCollection.AddSingleton(loggerFactory); + serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); }); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var source = sourceBuilder - .Append(DeltaSeeder.Create()) - .Build(default); + await using var sourceRepository = await GetReadOnlySourceRepository(serviceScope); - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.ReadOnly); + var source = CreateSource(new[] { 1ul }); // ACT @@ -531,41 +582,26 @@ private async Task committed.ShouldBeFalse(); - loggerVerifier.Invoke(Times.Once()); + logs.Count(log => log.Exception is not null).ShouldBe(1); + + //loggerVerifier.Invoke(Times.Once()); } - private async Task Generic_GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); + + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - var sourceId = Id.NewId(); - - var firstSourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var secondSourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var firstSource = firstSourceBuilder - .Append(DeltaSeeder.Create()) - .Build(sourceId); - - var secondSource = secondSourceBuilder - .Append(DeltaSeeder.Create()) - .Build(sourceId); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); + var firstSource = CreateSource(new[] { 1ul }); + var secondSource = CreateSource(new[] { 1ul }, id: firstSource.Id); // ACT @@ -578,9 +614,9 @@ private async Task Generic_GivenNonUniqueSourceIds_WhenCommittingSources_ThenSec secondSourceCommitted.ShouldBeFalse(); } - private async Task Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(SourcesAdder sourcesAdder) { // ARRANGE @@ -589,16 +625,11 @@ private async Task Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenRetur using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - var source = sourceBuilder - .Append(DeltaSeeder.Create()) - .Build(default); + var source = CreateSource(new[] { 1ul }); source = source with { @@ -608,233 +639,206 @@ private async Task Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenRetur .ToImmutableArray(), }; - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - // ARRANGE ASSERTIONS repeatCount.ShouldBeGreaterThan(1); // ACT - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await sourceRepository.Commit(source); // ASSERT - sourceCommitted.ShouldBeFalse(); + committed.ShouldBeFalse(); } - private async Task - Generic_GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); - var message = new Message - { - Id = Id.NewId(), - EntityPointer = Id.NewId() + Version.Zero, - Delta = new DoNothing(), - }; - - var source = SourceSeeder.Create(message); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); + var source = CreateSource(new[] { 0ul }); // ACT - var sourceCommitted = - await sourceRepository.Commit(source); + var sourceCommitted = await sourceRepository.Commit(source); // ASSERT sourceCommitted.ShouldBeTrue(); } - private async Task - Generic_GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged(SourcesAdder sourcesAdder) { // ARRANGE - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + //var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + var logs = new List(); + using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.RemoveAll(typeof(ILoggerFactory)); - serviceCollection.AddSingleton(loggerFactory); + serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); }); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + var entityId = Id.NewId(); - var firstSourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - var secondSourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - var firstSource = firstSourceBuilder - .Append(DeltaSeeder.Create()) - .Build(Id.NewId()); - - var secondSource = secondSourceBuilder - .Append(DeltaSeeder.Create()) - .Build(Id.NewId()); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); + var firstSource = CreateSource(new[] { 1ul }, entityId: entityId); + var secondSource = CreateSource(new[] { 1ul }, entityId: entityId); // ACT - var firstSourceCommitted = - await sourceRepository.Commit(firstSource); - var secondSourceCommitted = - await sourceRepository.Commit(secondSource); + var firstSourceCommitted = await sourceRepository.Commit(firstSource); + var secondSourceCommitted = await sourceRepository.Commit(secondSource); // ASSERT - firstSource.Messages.Length.ShouldBe(1); - secondSource.Messages.Length.ShouldBe(1); - - firstSource.Messages.ShouldAllBe(message => message.EntityPointer.Id == entityId); - secondSource.Messages.ShouldAllBe(message => message.EntityPointer.Id == entityId); - - firstSource.Messages[0].EntityPointer.Version - .ShouldBe(secondSource.Messages[0].EntityPointer.Version); - firstSourceCommitted.ShouldBeTrue(); secondSourceCommitted.ShouldBeFalse(); - loggerVerifier.Invoke(Times.Once()); + logs.Count(log => log.Exception is not null).ShouldBe(1); + + //loggerVerifier.Invoke(Times.Once()); } - private async Task Generic_GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + var tag = TagSeeder.Create(); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var source = sourceBuilder - .Append(new AddTag(tag)) - .Append(new AddTag(tag)) - .Build(default); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); + var source = new Source + { + Id = default, + TimeStamp = default, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = default, + EntityPointer = default, + Delta = new DoNothing(), + AddTags = new[] { tag, tag }.ToImmutableArray(), + }, + }.ToImmutableArray(), + }; // ACT - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await sourceRepository.Commit(source); // ASSERT - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); } - private async Task Generic_GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); + + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); var lease = LeaseSeeder.Create(); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var source = sourceBuilder - .Append(new AddLease(lease)) - .Append(new AddLease(lease)) - .Build(default); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); + var source = new Source + { + Id = default, + TimeStamp = default, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = default, + EntityPointer = default, + Delta = new DoNothing(), + AddLeases = new[] { lease, lease }.ToImmutableArray(), + }, + }.ToImmutableArray(), + }; // ACT - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await sourceRepository.Commit(source); // ASSERT - sourceCommitted.ShouldBeFalse(); + committed.ShouldBeFalse(); } - private async Task - Generic_GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature(SourcesAdder sourcesAdder) { // ARRANGE - const ulong expectedCount = 5; - using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); + + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); var expectedSourceId = Id.NewId(); var expectedEntityId = Id.NewId(); var expectedSourceTimeStamp = sourcesAdder.FixTimeStamp(TimeStamp.UtcNow); + var expectedAgentSignature = new UnknownAgentSignature(new Dictionary()); - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - var source = await BuildSource(serviceScope, expectedSourceId, expectedEntityId, - new[] { expectedCount }, expectedSourceTimeStamp, agentSignature); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var sourceCommitted = await sourceRepository.Commit(source); + var source = CreateSource + ( + new[] { 1ul }, + id: expectedSourceId, + timeStamp: expectedSourceTimeStamp, + entityId: expectedEntityId, + agentSignature: expectedAgentSignature + ); + + var committed = await sourceRepository.Commit(source); var query = new EntityQuery(expectedEntityId); // ARRANGE ASSERTIONS - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); // ACT - var annotatedAgentSignatures = - await sourceRepository.EnumerateAnnotatedAgentSignatures(query).ToArrayAsync(); + var annotatedAgentSignatures = await sourceRepository + .EnumerateAnnotatedAgentSignatures(query) + .ToArrayAsync(); // ASSERT @@ -844,41 +848,43 @@ private async Task annotatedAgentSignatures[0].SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); annotatedAgentSignatures[0].EntityPointers.Length.ShouldBe(1); annotatedAgentSignatures[0].EntityPointers[0].Id.ShouldBe(expectedEntityId); - annotatedAgentSignatures[0].Data.ShouldBeEquivalentTo(agentSignature); + annotatedAgentSignatures[0].Data.ShouldBeEquivalentTo(expectedAgentSignature); } - private async Task Generic_GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta(SourcesAdder sourcesAdder) { // ARRANGE - const ulong expectedDeltaCount = 5; - + ulong[] numbers = { 1, 2, 3, 4, 5 }; + using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); + + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); var expectedSourceId = Id.NewId(); var expectedEntityId = Id.NewId(); var expectedSourceTimeStamp = sourcesAdder.FixTimeStamp(TimeStamp.UtcNow); - var source = await BuildSource(serviceScope, expectedSourceId, expectedEntityId, - new[] { expectedDeltaCount }, expectedSourceTimeStamp); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); + var source = CreateSource + ( + numbers, + id: expectedSourceId, + timeStamp: expectedSourceTimeStamp, + entityId: expectedEntityId + ); - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await sourceRepository.Commit(source); var query = new GetDeltasQuery(expectedEntityId, default); // ARRANGE ASSERTIONS - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); // ACT @@ -886,90 +892,80 @@ private async Task Generic_GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenRe // ASSERT - annotatedDeltas.Length.ShouldBe(1); - - annotatedDeltas[0].SourceId.ShouldBe(expectedSourceId); - annotatedDeltas[0].SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); - annotatedDeltas[0].EntityPointer.Id.ShouldBe(expectedEntityId); - annotatedDeltas[0].EntityPointer.Version.ShouldBe(new Version(1)); - - var actualDeltaCount = annotatedDeltas[0].Data.ShouldBeAssignableTo().ShouldNotBeNull(); - - actualDeltaCount.Number.ShouldBe(expectedDeltaCount); - } - - private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE + annotatedDeltas.Length.ShouldBe(numbers.Length); - using var serviceScope = CreateServiceScope(serviceCollection => + foreach (var number in numbers) { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - var expectedEntity = TEntity.Construct(entityId).WithVersion(new Version(1)); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); - - var entityRepository = EntityRepository.Create(serviceScope.ServiceProvider, sourceRepository); - - var source = await BuildSource(serviceScope, Id.NewId(), entityId, - new[] { 0UL }); - - var sourceCommitted = await sourceRepository.Commit(source); - - // ARRANGE ASSERTIONS - - sourceCommitted.ShouldBeTrue(); - - // ACT - - var actualEntity = await entityRepository.GetSnapshot(entityId); - - // ASSERT - - actualEntity.ShouldBeEquivalentTo(expectedEntity); + var annotatedDelta = annotatedDeltas[Convert.ToInt32(number) - 1]; + + annotatedDelta.SourceId.ShouldBe(expectedSourceId); + annotatedDelta.SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); + annotatedDelta.EntityPointer.Id.ShouldBe(expectedEntityId); + annotatedDelta.EntityPointer.Version.ShouldBe(new Version(number)); + + annotatedDelta.Data + .ShouldBeAssignableTo() + .ShouldNotBeNull() + .Number + .ShouldBe(number); + } } - private async Task Generic_GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + var entityId = Id.NewId(); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - var tag = new Tag("Foo", "Bar"); + var tags = new[] { tag }.ToImmutableArray(); - var expectedInitialTags = new[] { tag }.ToImmutableArray(); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var initialSource = sourceBuilder - .Append(new AddTag(tag)) - .Build(Id.NewId()); + var initialSource = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = Id.NewId(), + Delta = new DoNothing(), + EntityPointer = entityId, + AddTags = tags, + }, + }.ToImmutableArray(), + }; var initialSourceCommitted = await sourceRepository.Commit(initialSource); + + var finalSource = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = Id.NewId(), + Delta = new DoNothing(), + EntityPointer = entityId, + DeleteTags = tags, + }, + }.ToImmutableArray(), + }; - var tagQuery = new DeleteTagsQuery(entityId, expectedInitialTags); + var tagQuery = new DeleteTagsQuery(entityId, tags); // ARRANGE ASSERTIONS @@ -977,58 +973,77 @@ private async Task Generic_GivenEntityCommittedWithTags_WhenRemovingAllTags_Then // ACT - var actualInitialTags = await sourceRepository.EnumerateTags(tagQuery).ToArrayAsync(); - - var finalSource = sourceBuilder - .Append(new DeleteTag(tag)) - .Build(Id.NewId()); + var initialTags = await sourceRepository + .EnumerateTags(tagQuery) + .ToArrayAsync(); var finalSourceCommitted = await sourceRepository.Commit(finalSource); - var actualFinalTags = await sourceRepository.EnumerateTags(tagQuery).ToArrayAsync(); + var finalTags = await sourceRepository + .EnumerateTags(tagQuery) + .ToArrayAsync(); // ASSERT + initialTags.Length.ShouldBe(1); + initialTags[0].ShouldBeEquivalentTo(tag); finalSourceCommitted.ShouldBeTrue(); - - expectedInitialTags.SequenceEqual(actualInitialTags).ShouldBeTrue(); - - actualFinalTags.ShouldBeEmpty(); + finalTags.ShouldBeEmpty(); } - private async Task Generic_GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); - var entityId = Id.NewId(); - - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + var lease = new Lease("Foo", "Bar", "Baz"); + var leases = new[] { lease }.ToImmutableArray(); - var expectedInitialLeases = new[] { lease }.ToImmutableArray(); - - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - - var initialSource = sourceBuilder - .Append(new AddLease(lease)) - .Build(Id.NewId()); - + var initialSource = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = Id.NewId(), + Delta = new DoNothing(), + EntityPointer = default, + AddLeases = leases, + } + }.ToImmutableArray(), + }; + var initialSourceCommitted = await sourceRepository.Commit(initialSource); + + var finalSource = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = Id.NewId(), + Delta = new DoNothing(), + EntityPointer = default, + DeleteLeases = leases, + }, + }.ToImmutableArray(), + }; - var leaseQuery = new DeleteLeasesQuery(expectedInitialLeases); + var leaseQuery = new DeleteLeasesQuery(leases); // ARRANGE ASSERTIONS @@ -1036,54 +1051,42 @@ private async Task Generic_GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ // ACT - var actualInitialLeases = await sourceRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); - - var finalSource = sourceBuilder - .Append(new DeleteLease(lease)) - .Build(Id.NewId()); - + var initialLeases = await sourceRepository + .EnumerateLeases(leaseQuery) + .ToArrayAsync(); + var finalSourceCommitted = await sourceRepository.Commit(finalSource); - var actualFinalLeases = await sourceRepository.EnumerateLeases(leaseQuery).ToArrayAsync(); + var finalLeases = await sourceRepository + .EnumerateLeases(leaseQuery) + .ToArrayAsync(); // ASSERT + initialLeases.Length.ShouldBe(1); finalSourceCommitted.ShouldBeTrue(); - - actualInitialLeases.SequenceEqual(expectedInitialLeases).ShouldBeTrue(); - - actualFinalLeases.ShouldBeEmpty(); + finalLeases.ShouldBeEmpty(); } - private async Task - Generic_GivenSourceCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenSourceCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); - var expectedDelta = new StoreNumber(1); + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); + var expectedDelta = new StoreNumber(1); - var source = sourceBuilder - .Append(expectedDelta) - .Build(Id.NewId()); + var source = CreateSource(new[] { 1ul }); var versionOneQuery = new EntityVersionQuery(new Version(1), new Version(1)); - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(TestSessionOptions.Write); - // ACT var sourceCommitted = await sourceRepository.Commit(source); @@ -1093,77 +1096,50 @@ private async Task // ASSERT sourceCommitted.ShouldBeTrue(); - - source.Messages.Length.ShouldBe(1); - - source.Messages[0].EntityPointer.Version.ShouldBe(new Version(1)); - newDeltas.Length.ShouldBe(1); - newDeltas[0].ShouldBeEquivalentTo(expectedDelta); } - private async Task - Generic_GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedDelta< - TEntity>(SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedDelta(SourcesAdder sourcesAdder) { // ARRANGE using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); - + + await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + var expectedDelta = new StoreNumber(2); - var sourceBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - var firstSource = sourceBuilder - .Append(new StoreNumber(1)) - .Build(Id.NewId()); - - var secondSource = sourceBuilder - .Append(expectedDelta) - .Build(Id.NewId()); + var entityId = Id.NewId(); + var firstSource = CreateSource(new[] { 1ul }, entityId: entityId); + var secondSource = CreateSource(new[] { 2ul }, entityId: entityId); var versionTwoQuery = new EntityVersionQuery(new Version(2), new Version(2)); - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService().CreateRepository(TestSessionOptions.Write); + // ACT var firstSourceCommitted = await sourceRepository.Commit(firstSource); - // ARRANGE ASSERTIONS - - firstSourceCommitted.ShouldBeTrue(); - - // ACT - var secondSourceCommitted = await sourceRepository.Commit(secondSource); var newDeltas = await sourceRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); // ASSERT + firstSourceCommitted.ShouldBeTrue(); secondSourceCommitted.ShouldBeTrue(); - - secondSource.Messages.Length.ShouldBe(1); - - secondSource.Messages[0].EntityPointer.Version.ShouldBe(new Version(2)); - newDeltas.Length.ShouldBe(1); - newDeltas[0].ShouldBeEquivalentTo(expectedDelta); } - private async Task - Generic_GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) { const ulong timeSpanInMinutes = 60UL; const ulong gteInMinutes = 20UL; @@ -1172,7 +1148,6 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); var originTimeStamp = TimeStamp.UnixEpoch; @@ -1195,9 +1170,7 @@ private async Task var agentSignature = new UnknownAgentSignature(new Dictionary()); var deltas = new object[] { new StoreNumber(i) }; - var leases = new[] { new CountLease(i) }; - var tags = new[] { new CountTag(i) }; expectedObjects.Add(i is >= gteInMinutes and <= lteInMinutes, currentSourceId, currentEntityId, @@ -1214,9 +1187,22 @@ private async Task break; } - var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, - new[] { i }, - currentSourceTimeStamp, agentSignature); + var source = new Source + { + Id = currentSourceId, + TimeStamp = currentSourceTimeStamp, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta =>new Message + { + Id = Id.NewId(), + EntityPointer = currentEntityId, + Delta = delta, + AddLeases = leases.ToImmutableArray(), + AddTags = tags.ToImmutableArray(), + }) + .ToImmutableArray(), + }; sources.Add(source); } @@ -1241,10 +1227,10 @@ private async Task await TestGetTags(serviceScope, query, expectedObjects); } - private async Task - Generic_GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( + SourcesAdder sourcesAdder) { const ulong numberOfSourceIds = 10UL; const ulong whichSourceId = 5UL; @@ -1252,7 +1238,6 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); var sources = new List(); @@ -1271,9 +1256,7 @@ private async Task var currentEntityId = entityIds[i - 1]; var deltas = new object[] { new StoreNumber(i) }; - var leases = new[] { new CountLease(i) }; - var tags = new[] { new CountTag(i) }; expectedObjects.Add(i == whichSourceId, currentSourceId, currentEntityId, agentSignature, @@ -1282,9 +1265,22 @@ private async Task if (i == whichSourceId) sourceId = currentSourceId; - var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, - new[] { i }, - agentSignatureOverride: agentSignature); + var source = new Source + { + Id = currentSourceId, + TimeStamp = TimeStamp.UtcNow, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta =>new Message + { + Id = Id.NewId(), + EntityPointer = currentEntityId, + Delta = delta, + AddLeases = leases.ToImmutableArray(), + AddTags = tags.ToImmutableArray(), + }) + .ToImmutableArray(), + }; sources.Add(source); } @@ -1308,10 +1304,9 @@ private async Task await TestGetTags(serviceScope, query, expectedObjects); } - private async Task - Generic_GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) { const ulong numberOfEntityIds = 10UL; const ulong whichEntityId = 5UL; @@ -1319,7 +1314,6 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); var sources = new List(); @@ -1347,10 +1341,23 @@ private async Task leases, tags); if (i == whichEntityId) entityId = currentEntityId; - - var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, - new[] { i }, - agentSignatureOverride: agentSignature); + + var source = new Source + { + Id = currentSourceId, + TimeStamp = TimeStamp.UtcNow, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta =>new Message + { + Id = Id.NewId(), + EntityPointer = currentEntityId, + Delta = delta, + AddLeases = leases.ToImmutableArray(), + AddTags = tags.ToImmutableArray(), + }) + .ToImmutableArray(), + }; sources.Add(source); } @@ -1374,10 +1381,9 @@ private async Task await TestGetTags(serviceScope, query, expectedObjects); } - private async Task - Generic_GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : IEntity + [Theory] + [MemberData(nameof(AddSource))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) { const ulong numberOfVersions = 20; const ulong gte = 5UL; @@ -1386,28 +1392,42 @@ private async Task using var serviceScope = CreateServiceScope(serviceCollection => { sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); }); var counts = new List(); var expectedObjects = new ExpectedObjects(); + var messages = new List(); + for (var i = 1UL; i <= numberOfVersions; i++) { var delta = new StoreNumber(i); - var leases = new[] { new CountLease(i) }; - var tags = new[] { new CountTag(i) }; counts.Add(i); expectedObjects.Add(i is >= gte and <= lte, default, default, default!, new[] { delta }, leases, tags); + + messages.Add(new Message + { + Id = Id.NewId(), + EntityPointer = default, + Delta = delta, + AddLeases = leases.ToImmutableArray(), + AddTags = tags.ToImmutableArray(), + }); } - - var source = await BuildSource(serviceScope, Id.NewId(), Id.NewId(), counts.ToArray()); - + + var source = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = messages.ToImmutableArray(), + }; + var sources = new List { source }; var query = new EntityVersionQuery(new Version(gte), new Version(lte)); @@ -1418,292 +1438,14 @@ private async Task await TestGetTags(serviceScope, query, expectedObjects); } - private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TOptions : class - where TEntity : IEntity - { - const ulong countTo = 20UL; - const ulong gte = 5UL; - const ulong lte = 15UL; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var sources = new List(); - var expectedObjects = new ExpectedObjects(); - - var sourceIds = GetSortedIds((int)countTo); - var entityIds = GetSortedIds((int)countTo); - - var agentSignature = new UnknownAgentSignature(new Dictionary()); - - var deltas = new object[] { new DoNothing() }; - - for (var i = 1UL; i <= countTo; i++) - { - var currentSourceId = sourceIds[i - 1]; - var currentEntityId = entityIds[i - 1]; - - var leases = new[] { new CountLease(i) }; - - var tags = new[] { new CountTag(i) }; - - expectedObjects.Add(i is >= gte and <= lte, currentSourceId, currentEntityId, agentSignature, deltas, - leases, tags); - - var source = await BuildSource(serviceScope, currentSourceId, currentEntityId, - new[] { i }, - agentSignatureOverride: agentSignature); - - sources.Add(source); - } - - var options = serviceScope.ServiceProvider - .GetRequiredService>() - .Create("Count"); - - var query = new CountQuery(gte, lte, options); - - await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetLeases(serviceScope, query, expectedObjects); - await TestGetTags(serviceScope, query, expectedObjects); - } - - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenReadOnlyMode_WhenCommittingSource_ThenCannotWriteInReadOnlyModeExceptionIsLogged( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(SourcesAdder sourcesAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourcesAdder sourcesAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse(SourcesAdder sourcesAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity(SourcesAdder sourcesAdder, - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedDelta( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } - ); - } - [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(AddSource))] + public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) { return RunGenericTestAsync ( - new[] { sourcesAdder.QueryOptionsType, entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { sourcesAdder.QueryOptionsType }, + new object?[] { sourcesAdder } ); } diff --git a/test/EntityDb.Common.Tests/TestSessionOptions.cs b/test/EntityDb.Common.Tests/TestSessionOptions.cs index f8bdd058..202b06b7 100644 --- a/test/EntityDb.Common.Tests/TestSessionOptions.cs +++ b/test/EntityDb.Common.Tests/TestSessionOptions.cs @@ -2,6 +2,8 @@ public static class TestSessionOptions { + public const string Default = nameof(Default); + public const string Write = nameof(Write); public const string ReadOnly = nameof(ReadOnly); public const string ReadOnlySecondaryPreferred = nameof(ReadOnlySecondaryPreferred); diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index c5b691d5..026b94c4 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Reflection; +using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Snapshots; @@ -330,6 +331,80 @@ protected IServiceScope CreateServiceScope(Action? configure return new TestServiceScope(singletonServiceProvider, serviceScopeFactory.CreateScope()); } + public record Log + { + public required object[] ScopesStates { get; init; } + public required LogLevel LogLevel { get; init; } + public required EventId EventId { get; init; } + public required object? State { get; init; } + public required Exception? Exception { get; init; } + } + + private class Logger : ILogger + { + private readonly List _logs; + private readonly Stack _scopeStates = new(); + + public Logger(List logs) + { + _logs = logs; + } + + private class Scope : IDisposable + { + private readonly Logger _logger; + + public Scope(Logger logger) + { + _logger = logger; + } + + void IDisposable.Dispose() + { + _logger._scopeStates.Pop(); + } + } + + IDisposable ILogger.BeginScope(TState state) + { + _scopeStates.Push(state); + + return new Scope(this); + } + + bool ILogger.IsEnabled(LogLevel logLevel) + { + return true; + } + + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + _logs.Add(new Log + { + ScopesStates = _scopeStates.ToArray(), + LogLevel = logLevel, + EventId = eventId, + State = state, + Exception = exception, + }); + } + } + + + protected static ILoggerFactory GetMockedLoggerFactory(List logs) + { + var loggerFactoryMock = new Mock(MockBehavior.Strict); + + loggerFactoryMock + .Setup(factory => factory.CreateLogger(It.IsAny())) + .Returns(new Logger(logs)); + + loggerFactoryMock + .Setup(factory => factory.AddProvider(It.IsAny())); + + return loggerFactoryMock.Object; + } + protected static (ILoggerFactory Logger, Action LoggerVerifier) GetMockedLoggerFactory() where TException : Exception { @@ -397,11 +472,91 @@ void Verifier(Times times) } } - protected static void SetupSourceRepositoryMock(Mock sourceRepositoryMock, - List committedSources) + protected static ISourceSubscriber GetMockedSourceSubscriber(List committedSources) + { + var sourceSubscriberMock = new Mock(); + + sourceSubscriberMock + .Setup(subscriber => subscriber.Notify(It.IsAny())) + .Callback((Source source) => + { + committedSources.Add(source); + }); + + return sourceSubscriberMock.Object; + } + + protected static Task> GetWriteEntityRepository(IServiceScope serviceScope, bool snapshots) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateMultiple + ( + TestSessionOptions.Default, + TestSessionOptions.Write, + snapshots ? TestSessionOptions.Write : null + ); + } + + protected static Task> GetReadOnlyEntityRepository(IServiceScope serviceScope, bool snapshots) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateMultiple + ( + TestSessionOptions.Default, + TestSessionOptions.ReadOnly, + snapshots ? TestSessionOptions.ReadOnly : null + ); + + } + + protected static Task> GetWriteSnapshotRepository( + IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateRepository(TestSessionOptions.Write); + } + + protected static Task> GetReadOnlySnapshotRepository( + IServiceScope serviceScope, bool secondaryPreferred = false) { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateRepository(secondaryPreferred ? TestSessionOptions.ReadOnlySecondaryPreferred : TestSessionOptions.ReadOnly); + } + + protected static Task GetWriteSourceRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository + ( + TestSessionOptions.Write + ); } + protected static Task GetReadOnlySourceRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .CreateRepository + ( + TestSessionOptions.ReadOnly + ); + } + + protected static Task> GetReadOnlyProjectionRepository(IServiceScope serviceScope, bool snapshots) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateRepository + ( + snapshots ? TestSessionOptions.Write : null + ); + } + protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( Mock sourceRepositoryMock, List? committedSources = null) { From 5f8d36d58b385c11ed7dd4363a7a3b9bcee43186 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Tue, 9 Jan 2024 22:03:32 -0800 Subject: [PATCH 72/93] another big refactor --- .DS_Store | Bin 6148 -> 0 bytes src/.editorconfig => .editorconfig | 2 +- Directory.Build.props | 3 +- EntityDb.sln | 4 + LICENSE.txt | 2 +- README.md | 104 +- SECURITY.md | 11 +- global.json | 2 +- src/Directory.Build.props | 2 +- .../Entities/Deltas/IAddLeasesDelta.cs | 18 - .../Entities/Deltas/IAddTagsDelta.cs | 18 - .../Entities/Deltas/IDeleteLeasesDelta.cs | 18 - .../Entities/Deltas/IDeleteTagsDelta.cs | 18 - src/EntityDb.Abstractions/Entities/IEntity.cs | 4 +- .../Entities/IEntityRepositoryFactory.cs | 18 +- .../Entities/IMultipleEntityRepository.cs | 25 +- .../Entities/ISingleEntityRepository.cs | 23 +- .../EntityDb.Abstractions.csproj | 2 +- .../EventStreams/IEventStreamRepository.cs | 39 - .../IEventStreamRepositoryFactory.cs | 17 - .../Projections/IProjection.cs | 13 +- .../Projections/IProjectionRepository.cs | 14 +- .../IProjectionRepositoryFactory.cs | 4 +- .../Snapshots/ISnapshot.cs | 40 - .../Snapshots/ISnapshotRepository.cs | 40 - .../Snapshots/ISnapshotRepositoryFactory.cs | 19 - ...eGroupData.cs => IAnnotatedMessageData.cs} | 12 +- .../Annotations/IAnnotatedSourceData.cs | 10 +- .../Sources/ISourceRepository.cs | 68 +- .../Sources/ISourceRepositoryFactory.cs | 2 +- src/EntityDb.Abstractions/Sources/Message.cs | 15 +- ...rBuilder.cs => ILeaseDataFilterBuilder.cs} | 4 +- ...uilder.cs => IMessageDataFilterBuilder.cs} | 38 +- ...Builder.cs => ISourceDataFilterBuilder.cs} | 14 +- ...terBuilder.cs => ITagDataFilterBuilder.cs} | 4 +- .../Sources/Queries/ILeaseQuery.cs | 2 +- ...{IMessageQuery.cs => IMessageDataQuery.cs} | 6 +- ...ssageGroupQuery.cs => ISourceDataQuery.cs} | 6 +- .../Sources/Queries/ITagQuery.cs | 2 +- .../Queries/SortBuilders/ILeaseSortBuilder.cs | 4 +- ...tBuilder.cs => IMessageDataSortBuilder.cs} | 14 +- ...rtBuilder.cs => ISourceDataSortBuilder.cs} | 8 +- .../Queries/SortBuilders/ITagSortBuilder.cs | 4 +- src/EntityDb.Abstractions/Sources/Source.cs | 3 +- .../{Sources => States}/Attributes/ILease.cs | 6 +- .../{Sources => States}/Attributes/ITag.cs | 4 +- .../States/Deltas/IAddLeasesDelta.cs | 17 + .../States/Deltas/IAddTagsDelta.cs | 17 + .../States/Deltas/IDeleteLeasesDelta.cs | 17 + .../States/Deltas/IDeleteTagsDelta.cs | 17 + src/EntityDb.Abstractions/States/IState.cs | 40 + .../States/IStateRepository.cs | 40 + .../States/IStateRepositoryFactory.cs | 19 + .../Transforms/IMutator.cs | 2 +- .../Transforms/IReducer.cs | 2 +- .../Streams/IMultipleStreamRepository.cs | 41 + .../Streams/ISingleStreamRepository.cs | 37 + .../Streams/IStreamRepositoryFactory.cs | 37 + src/EntityDb.Abstractions/ValueObjects/Id.cs | 2 +- src/EntityDb.Abstractions/ValueObjects/Key.cs | 2 +- .../ValueObjects/Pointer.cs | 6 +- .../ValueObjects/Version.cs | 8 +- src/EntityDb.Abstractions/packages.lock.json | 16 + .../Entities/EntityRepositoryFactory.cs | 38 +- .../Entities/MultipleEntityRepository.cs | 91 +- .../Entities/SingleEntityRepository.cs | 10 +- src/EntityDb.Common/EntityDb.Common.csproj | 2 +- .../EventStreams/EventStreamRepository.cs | 139 -- .../EventStreamRepositoryFactory.cs | 40 - .../Exceptions/CannotResolveTypeException.cs | 4 +- .../DataDeserializationException.cs | 6 + .../Exceptions/DataSerializationException.cs | 6 + .../Exceptions/DeserializeException.cs | 9 - .../EntityAlreadyLoadedException.cs | 11 - .../Exceptions/ExistingEntityException.cs | 10 + .../Exceptions/ExistingStreamException.cs | 9 + .../Exceptions/NoAgentException.cs | 4 +- .../OptimisticConcurrencyException.cs | 2 +- ...Exception.cs => ReadOnlyWriteException.cs} | 4 +- .../Exceptions/SerializeException.cs | 9 - .../SnapshotPointerDoesNotExistException.cs | 8 - .../Exceptions/StateDoesNotExistException.cs | 11 + .../Exceptions/UnknownEntityIdException.cs | 10 + .../Exceptions/UnknownStreamException.cs | 9 + .../Extensions/ServiceCollectionExtensions.cs | 40 +- .../SnapshotRepositoryFactoryExtensions.cs | 20 - .../Projections/ProjectionRepository.cs | 22 +- .../ProjectionRepositoryFactory.cs | 18 +- .../Snapshots/SnapshotRepositoryWrapper.cs | 45 - .../Snapshots/TestModeSnapshotManager.cs | 55 - .../Snapshots/TestModeSnapshotRepository.cs | 47 - .../TestModeSnapshotRepositoryFactory.cs | 34 - .../Annotations/AnnotatedMessageData.cs | 36 + .../Annotations/AnnotatedSourceData.cs | 12 +- .../Annotations/AnnotatedSourceGroupData.cs | 36 - src/EntityDb.Common/Sources/Attributes/Tag.cs | 6 - .../Sources/Documents/DocumentsExtensions.cs | 16 +- ...ageDocument.cs => IMessageDataDocument.cs} | 4 +- ...roupDocument.cs => ISourceDataDocument.cs} | 4 +- ...essor.cs => EntityStateSourceProcessor.cs} | 36 +- .../ProjectionSnapshotSourceProcessor.cs | 80 - .../ProjectionStateSourceProcessor.cs | 80 + .../Queues}/SourceProcessorQueueExtensions.cs | 4 +- .../Sources/PublishSourceRepository.cs | 2 +- .../Queries/Modified/ModifiedLeaseQuery.cs | 11 +- .../Modified/ModifiedMessageDataQuery.cs | 32 + .../Modified/ModifiedMessageGroupQuery.cs | 32 - .../Queries/Modified/ModifiedMessageQuery.cs | 31 - .../Queries/Modified/ModifiedQueryBase.cs | 5 +- .../Modified/ModifiedSourceDataQuery.cs | 32 + .../Queries/Modified/ModifiedTagQuery.cs | 11 +- .../Sources/Queries/QueryExtensions.cs | 32 +- .../SortBuilders/ReverseLeaseSortBuilder.cs | 15 +- .../ReverseMessageDataSortBuilder.cs | 20 + .../ReverseMessageGroupSortBuilder.cs | 14 - .../SortBuilders/ReverseMessageSortBuilder.cs | 18 - .../SortBuilders/ReverseSortBuilderBase.cs | 4 +- .../ReverseSourceDataSortBuilder.cs | 15 + .../SortBuilders/ReverseTagSortBuilder.cs | 14 +- .../SortBuilders}/SortBuilderExtensions.cs | 27 +- .../Queries/Standard/DeleteLeasesQuery.cs | 6 +- .../Queries/Standard/DeleteTagsQuery.cs | 8 +- .../Queries/Standard/GetDeltasQuery.cs | 16 +- .../Standard/GetLastEntityVersionQuery.cs | 23 - .../Standard/GetLastStateVersionQuery.cs | 23 + .../Queries/Standard/GetSourceQuery.cs | 22 +- .../Queries/Standard/MatchingLeaseQuery.cs | 4 +- .../Queries/Standard/MatchingLeasesQuery.cs | 4 +- .../BufferBlockSourceReprocessorQueue.cs | 3 +- .../ISourceReprocessorQueueItem.cs | 2 +- .../TestModeSourceReprocessorQueue.cs | 3 +- .../SourceRepositoryExtensions.cs | 11 +- .../Sources/SourceRepositoryWrapper.cs | 44 +- ...iber.cs => EntityStateSourceSubscriber.cs} | 7 +- ....cs => ProjectionStateSourceSubscriber.cs} | 9 +- .../{Sources => States}/Attributes/Lease.cs | 4 +- src/EntityDb.Common/States/Attributes/Tag.cs | 6 + .../StateRepositoryFactoryExtensions.cs | 19 + .../States/StateRepositoryWrapper.cs | 45 + .../States/TestModeStateManager.cs | 55 + .../States/TestModeStateRepository.cs | 47 + .../States/TestModeStateRepositoryFactory.cs | 34 + .../TryCatchStateRepository.cs} | 26 +- .../Streams/MultipleStreamRepository.cs | 128 ++ .../Streams/SingleEntityRepository.cs | 36 + src/EntityDb.Common/Streams/Stream.cs | 10 + .../Streams/StreamRepositoryFactory.cs | 57 + src/EntityDb.Common/packages.lock.json | 22 + .../Converters/IdConverter.cs | 15 - .../Converters/TimeStampConverter.cs | 15 - .../Converters/VersionNumberConverter.cs | 15 - .../DbContexts/EntityDbContextBase.cs | 27 - .../DbContexts/EntityDbContextFactory.cs | 28 - .../DbContexts/IEntityDbContext.cs | 20 - .../DbContexts/IEntityDbContextFactory.cs | 20 - .../EntityDb.EntityFramework.csproj | 17 - .../Extensions/ServiceCollectionExtensions.cs | 47 - ...bTransactionRepositoryFactoryExtensions.cs | 18 - .../Predicates/PredicateBuilder.cs | 40 - .../Sessions/EntityFrameworkSession.cs | 195 -- .../EntityFrameworkSnapshotSessionOptions.cs | 39 - .../Sessions/IEntityFrameworkSession.cs | 22 - .../TestModeEntityFrameworkSession.cs | 48 - .../EntityFrameworkSnapshotRepository.cs | 61 - ...ntityFrameworkSnapshotRepositoryFactory.cs | 53 - .../Snapshots/IEntityFrameworkSnapshot.cs | 17 - ...ntityFrameworkSnapshotRepositoryFactory.cs | 24 - .../Snapshots/SnapshotReference.cs | 40 - .../SnapshotReferenceTypeConfiguration.cs | 46 - ...ntityFrameworkSnapshotRepositoryFactory.cs | 89 - .../packages.lock.json | 137 -- .../EntityDb.InMemory.csproj | 13 - .../Extensions/ServiceCollectionExtensions.cs | 44 - .../Sessions/IInMemorySession.cs | 12 - .../Sessions/InMemorySession.cs | 32 - .../Sessions/InMemorySessionOptions.cs | 12 - .../Sessions/ReadOnlyInMemorySession.cs | 29 - .../Snapshots/InMemorySnapshotRepository.cs | 32 - .../InMemorySnapshotRepositoryFactory.cs | 45 - src/EntityDb.InMemory/packages.lock.json | 34 - .../Envelopes/JsonEnvelopeService.cs | 4 +- src/EntityDb.Json/packages.lock.json | 29 + .../Documents/AgentSignatureDocument.cs | 34 +- .../Commands/DeleteDocumentsCommand.cs | 7 +- ...{DeltaDocument.cs => DeltaDataDocument.cs} | 54 +- .../Envelopes/MongoDbEnvelopeService.cs | 4 +- ...{LeaseDocument.cs => LeaseDataDocument.cs} | 43 +- .../Documents/MessageDataDocumentBase.cs | 19 + .../Documents/MessageDocumentBase.cs | 19 - .../Documents/MessageGroupDocumentBase.cs | 20 - .../Documents/Queries/DocumentQuery.cs | 15 +- .../Queries/DocumentQueryExtensions.cs | 27 +- .../Documents/SourceDataDocumentBase.cs | 20 + .../{SnapshotDocument.cs => StateDocument.cs} | 8 +- .../{TagDocument.cs => TagDataDocument.cs} | 45 +- src/EntityDb.MongoDb/EntityDb.MongoDb.csproj | 6 +- .../Extensions/MongoClientExtensions.cs | 52 +- .../Extensions/ServiceCollectionExtensions.cs | 16 +- .../IMongoDbSnapshotRepositoryFactory.cs | 24 - .../Snapshots/MongoDbSnapshotRepository.cs | 82 - ...goDbSnapshotRepositoryFactoryExtensions.cs | 27 - ...MongoDbSnapshotRepositoryFactoryWrapper.cs | 41 - .../Snapshots/Sessions/IMongoSession.cs | 24 - .../Sessions/TestModeMongoSession.cs | 54 - .../IMongoDbSourceRepositoryFactory.cs | 2 +- .../Sources/MongoDbSourceRepository.cs | 97 +- .../Sources/MongoDbSourceRepositoryFactory.cs | 2 +- ...erBuilder.cs => LeaseDataFilterBuilder.cs} | 10 +- .../MessageDataFilterBuilder.cs | 32 + .../FilterBuilders/MessageFilterBuilder.cs | 32 - .../MessageGroupFilterBuilder.cs | 20 - .../FilterBuilders/SourceDataFilterBuilder.cs | 20 + ...lterBuilder.cs => TagDataFilterBuilder.cs} | 8 +- .../Queries/SortBuilders/LeaseSortBuilder.cs | 6 +- .../SortBuilders/MessageGroupSortBuilder.cs | 15 - .../SortBuilders/MessageSortBuilder.cs | 10 +- .../SortBuilders/SourceDataSortBuilder.cs | 15 + .../Queries/SortBuilders/TagSortBuilder.cs | 4 +- .../Sources/Sessions/MongoSession.cs | 2 +- ...ProvisionMongoDbStateRepositoryFactory.cs} | 28 +- .../States/IMongoDbStateRepositoryFactory.cs | 24 + .../States/MongoDbStateRepository.cs | 82 + .../MongoDbStateRepositoryFactory.cs} | 32 +- ...MongoDbStateRepositoryFactoryExtensions.cs | 27 + .../MongoDbStateRepositoryFactoryWrapper.cs | 41 + .../States/Sessions/IMongoSession.cs | 24 + .../Sessions/MongoDbStateSessionOptions.cs} | 12 +- .../Sessions/MongoSession.cs | 46 +- .../States/Sessions/TestModeMongoSession.cs | 54 + .../TestModeMongoDbStateRepositoryFactory.cs} | 16 +- src/EntityDb.MongoDb/packages.lock.json | 181 +- src/EntityDb.Mvc/EntityDb.Mvc.csproj | 2 +- src/EntityDb.Mvc/packages.lock.json | 29 + .../Converters/NpgsqlConverter.cs | 283 --- src/EntityDb.Npgsql/EntityDb.Npgsql.csproj | 16 - .../Extensions/NpgsqlConnectionExtensions.cs | 93 - ...lTransactionRepositoryFactoryExtensions.cs | 17 - .../Extensions/ServiceCollectionExtensions.cs | 55 - .../Properties/AssemblyInfo.cs | 5 - .../Queries/NpgsqlQueryOptions.cs | 20 - ...isionNpgsqlTransactionRepositoryFactory.cs | 83 - .../NpgsqlTransactionRepositoryFactory.cs | 64 - src/EntityDb.Npgsql/packages.lock.json | 63 - .../Atlas/Cluster/CreateCollections.cs | 2 +- .../MongoDb/Atlas/Cluster/CreateRole.cs | 6 +- .../EntityDb.Provisioner.csproj | 2 +- src/EntityDb.Provisioner/packages.lock.json | 208 +- src/EntityDb.Redis/EntityDb.Redis.csproj | 6 +- .../Extensions/ServiceCollectionExtensions.cs | 15 +- .../Snapshots/RedisSnapshotRepository.cs | 56 - .../Snapshots/Sessions/IRedisSession.cs | 12 - .../States/RedisStateRepository.cs | 56 + .../RedisStateRepositoryFactory.cs} | 28 +- .../States/Sessions/IRedisSession.cs | 12 + .../Sessions/RedisSession.cs | 42 +- .../Sessions/RedisStateSessionOptions.cs} | 14 +- src/EntityDb.Redis/packages.lock.json | 76 +- .../Commands/DeleteDocumentsCommand.cs | 18 - .../Commands/InsertDocumentsCommand.cs | 19 - .../Converters/ISqlConverter.cs | 30 - .../AgentSignatureDataDocumentReader.cs | 23 - .../AgentSignature/AgentSignatureDocument.cs | 90 - .../AgentSignatureDocumentReader.cs | 34 - .../AgentSignatureDocumentReaderBase.cs | 25 - .../AgentSignatureEntityIdsDocumentReader.cs | 26 - ...entSignatureTransactionIdDocumentReader.cs | 24 - .../Command/CommandDataDocumentReader.cs | 23 - .../Documents/Command/CommandDocument.cs | 109 - .../Command/CommandDocumentReader.cs | 34 - .../Command/CommandDocumentReaderBase.cs | 27 - .../Command/CommandEntityIdDocumentReader.cs | 24 - ...ommandEntityVersionNumberDocumentReader.cs | 24 - .../CommandTransactionIdDocumentReader.cs | 24 - src/EntityDb.SqlDb/Documents/DocumentBase.cs | 15 - .../Documents/IDocumentReader.cs | 13 - .../Documents/IEntitiesDocument.cs | 11 - .../Documents/IEntityDocument.cs | 11 - .../Documents/ITransactionDocument.cs | 18 - .../Lease/LeaseDataDocumentReader.cs | 23 - .../Documents/Lease/LeaseDocument.cs | 111 - .../Documents/Lease/LeaseDocumentReader.cs | 40 - .../Lease/LeaseDocumentReaderBase.cs | 33 - .../Lease/LeaseEntityIdDocumentReader.cs | 24 - .../Lease/LeaseTransactionIdDocumentReader.cs | 24 - .../Documents/Tag/TagDataDocumentReader.cs | 23 - .../Documents/Tag/TagDocument.cs | 107 - .../Documents/Tag/TagDocumentReader.cs | 38 - .../Documents/Tag/TagDocumentReaderBase.cs | 31 - .../Tag/TagEntityIdDocumentReader.cs | 24 - .../Tag/TagTransactionIdDocumentReader.cs | 24 - src/EntityDb.SqlDb/EntityDb.SqlDb.csproj | 8 - .../Extensions/DocumentQueryExtensions.cs | 155 -- ...bTransactionRepositoryFactoryExtensions.cs | 18 - src/EntityDb.SqlDb/Properties/AssemblyInfo.cs | 7 - .../Definitions/Filter/AndFilterDefinition.cs | 3 - .../Filter/AnyInFilterDefinition.cs | 5 - .../Definitions/Filter/EqFilterDefinition.cs | 3 - .../Definitions/Filter/GteFilterDefinition.cs | 3 - .../Definitions/Filter/IFilterDefinition.cs | 3 - .../Definitions/Filter/InFilterDefinition.cs | 5 - .../Definitions/Filter/LteFilterDefinition.cs | 3 - .../Definitions/Filter/NotFilterDefinition.cs | 3 - .../Definitions/Filter/OrFilterDefinition.cs | 3 - .../Definitions/Sort/AscSortDefinition.cs | 3 - .../Definitions/Sort/CombineSortDefinition.cs | 3 - .../Definitions/Sort/DescSortDefinition.cs | 3 - .../Definitions/Sort/ISortDefinition.cs | 3 - src/EntityDb.SqlDb/Queries/DocumentQuery.cs | 23 - .../AgentSignatureFilterBuilder.cs | 24 - .../FilterBuilders/CommandFilterBuilder.cs | 29 - .../FilterBuilders/FilterBuilderBase.cs | 77 - .../FilterBuilders/LeaseFilterBuilder.cs | 44 - .../FilterBuilders/TagFilterBuilder.cs | 39 - .../SortBuilders/AgentSignatureSortBuilder.cs | 18 - .../SortBuilders/CommandSortBuilder.cs | 23 - .../Queries/SortBuilders/LeaseSortBuilder.cs | 38 - .../Queries/SortBuilders/SortBuilderBase.cs | 36 - .../Queries/SortBuilders/TagSortBuilder.cs | 33 - src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs | 46 - src/EntityDb.SqlDb/Sessions/SqlDbSession.cs | 229 -- .../SqlDbTransactionSessionOptions.cs | 48 - .../Sessions/TestModeSqlDbSession.cs | 72 - .../ISqlDbTransactionRepositoryFactory.cs | 25 - .../SqlDbTransactionRepository.cs | 231 -- ...qlDbTransactionRepositoryFactoryWrapper.cs | 42 - ...stModeSqlDbTransactionRepositoryFactory.cs | 50 - src/EntityDb.SqlDb/packages.lock.json | 41 - src/EntityDb.Void/EntityDb.Void.csproj | 13 - .../Extensions/ServiceCollectionExtensions.cs | 25 - .../Transactions/VoidSourceRepository.cs | 104 - .../VoidSourceRepositoryFactory.cs | 16 - src/EntityDb.Void/packages.lock.json | 34 - test/.DS_Store | Bin 6148 -> 0 bytes test/Directory.Build.props | 12 +- .../DatabaseContainerCollection.cs | 2 +- .../DatabaseContainerFixture.cs | 27 +- .../Entities/EntityRepositoryTests.cs | 192 +- .../Entities/EntityTests.cs | 302 ++- .../EntityDb.Common.Tests.csproj | 5 +- .../Envelopes/EnvelopeTestsBase.cs | 22 +- .../EventStreams/EventStreamTests.cs | 335 --- .../FilterBuilderExtensionsTests.cs | 2 +- test/EntityDb.Common.Tests/IStartup.cs | 2 +- .../Implementations/Deltas/AddLease.cs | 13 - .../Implementations/Deltas/AddTag.cs | 13 - .../Implementations/Deltas/DeleteLease.cs | 13 - .../Implementations/Deltas/DeleteTag.cs | 13 - .../Entities/Deltas/AddLease.cs | 12 + .../Implementations/Entities/Deltas/AddTag.cs | 12 + .../Entities/Deltas/DeleteLease.cs | 12 + .../Entities/Deltas/DeleteTag.cs | 12 + .../{ => Entities}/Deltas/DoNothing.cs | 7 +- .../{ => Entities}/Deltas/StoreNumber.cs | 18 +- .../Implementations/Entities/TestEntity.cs | 34 +- .../Projections/OneToOneProjection.cs | 50 +- .../Implementations/Queries/EntityQuery.cs | 66 - .../Queries/EntityVersionQuery.cs | 56 - .../Implementations/Seeders/DeltaSeeder.cs | 85 - .../Seeders/EntityRepositoryExtensions.cs | 7 +- .../Implementations/Seeders/LeaseSeeder.cs | 6 +- .../Implementations/Seeders/MessageSeeder.cs | 29 - .../Implementations/Seeders/SourceSeeder.cs | 33 - .../Implementations/Seeders/TagSeeder.cs | 6 +- .../Snapshots/ISnapshotWithTestLogic.cs | 16 - .../{ => Sources}/Queries/CountQuery.cs | 11 +- .../{ => Sources}/Queries/SourceIdQuery.cs | 42 +- .../Queries/SourceTimeStampQuery.cs | 44 +- .../Sources/Queries/StateQuery.cs | 66 + .../Sources/Queries/StateVersionQuery.cs | 56 + .../Attributes}/CountLease.cs | 6 +- .../{Tags => States/Attributes}/CountTag.cs | 6 +- .../States/IStateWithTestLogic.cs | 12 + .../Projections/ProjectionsTests.cs | 100 +- .../Properties/AssemblyInfo.cs | 4 +- .../Snapshots/SnapshotTests.cs | 221 -- .../TryCatchSnapshotRepositoryTests.cs | 82 - .../Agents/AgentAccessorTestsBase.cs | 30 +- .../Agents/UnknownAgentAccessorTests.cs | 9 +- .../EntitySnapshotSourceSubscriberTests.cs | 123 -- .../EntityStateSourceSubscriberTests.cs | 119 ++ .../Sources/SourceTests.cs | 674 +++--- .../Sources/TryCatchSourceRepositoryTests.cs | 66 +- test/EntityDb.Common.Tests/Startup.cs | 2 +- test/EntityDb.Common.Tests/StartupBase.cs | 2 +- .../States/StateTests.cs | 216 ++ .../States/TryCatchStateRepositoryTests.cs | 85 + .../Streams/StreamTests.cs | 341 +++ test/EntityDb.Common.Tests/TestLogger.cs | 2 +- .../TestSessionOptions.cs | 4 +- test/EntityDb.Common.Tests/TestsBase.cs | 540 +++-- .../TypeResolvers/DefaultTypeResolverTests.cs | 2 +- .../TypeResolvers/LifoTypeResolverTests.cs | 14 +- .../MemberInfoNameTypeResolverTests.cs | 5 +- test/EntityDb.Common.Tests/packages.lock.json | 1854 +++++++++++++++- .../Envelopes/BsonDocumentEnvelopeTests.cs | 6 +- .../Properties/AssemblyInfo.cs | 2 +- .../Sessions/MongoSessionTests.cs | 18 +- test/EntityDb.MongoDb.Tests/Startup.cs | 2 +- .../EntityDb.MongoDb.Tests/packages.lock.json | 1881 +++++++++++++++- .../Agents/HttpContextAgentAccessorTests.cs | 14 +- .../Agents/HttpContextAgentSignatureTests.cs | 23 +- .../Properties/AssemblyInfo.cs | 2 +- .../Seeder/HttpContextSeeder.cs | 2 +- .../Seeder/HttpContextSeederOptions.cs | 2 +- test/EntityDb.Mvc.Tests/Startup.cs | 2 +- test/EntityDb.Mvc.Tests/packages.lock.json | 1888 ++++++++++++++++- .../Envelopes/JsonElementEnvelopeTests.cs | 6 +- .../Properties/AssemblyInfo.cs | 2 +- .../Sessions/RedisSessionTests.cs | 16 +- test/EntityDb.Redis.Tests/Startup.cs | 2 +- test/EntityDb.Redis.Tests/packages.lock.json | 1881 +++++++++++++++- 411 files changed, 12051 insertions(+), 9654 deletions(-) delete mode 100644 .DS_Store rename src/.editorconfig => .editorconfig (99%) delete mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs delete mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs delete mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs delete mode 100644 src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs delete mode 100644 src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs delete mode 100644 src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs delete mode 100644 src/EntityDb.Abstractions/Snapshots/ISnapshot.cs delete mode 100644 src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs delete mode 100644 src/EntityDb.Abstractions/Snapshots/ISnapshotRepositoryFactory.cs rename src/EntityDb.Abstractions/Sources/Annotations/{IAnnotatedSourceGroupData.cs => IAnnotatedMessageData.cs} (67%) rename src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/{ILeaseFilterBuilder.cs => ILeaseDataFilterBuilder.cs} (90%) rename src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/{IMessageFilterBuilder.cs => IMessageDataFilterBuilder.cs} (50%) rename src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/{IMessageGroupFilterBuilder.cs => ISourceDataFilterBuilder.cs} (62%) rename src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/{ITagFilterBuilder.cs => ITagDataFilterBuilder.cs} (87%) rename src/EntityDb.Abstractions/Sources/Queries/{IMessageQuery.cs => IMessageDataQuery.cs} (82%) rename src/EntityDb.Abstractions/Sources/Queries/{IMessageGroupQuery.cs => ISourceDataQuery.cs} (82%) rename src/EntityDb.Abstractions/Sources/Queries/SortBuilders/{IMessageSortBuilder.cs => IMessageDataSortBuilder.cs} (74%) rename src/EntityDb.Abstractions/Sources/Queries/SortBuilders/{IMessageGroupSortBuilder.cs => ISourceDataSortBuilder.cs} (75%) rename src/EntityDb.Abstractions/{Sources => States}/Attributes/ILease.cs (80%) rename src/EntityDb.Abstractions/{Sources => States}/Attributes/ITag.cs (61%) create mode 100644 src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs create mode 100644 src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs create mode 100644 src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs create mode 100644 src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs create mode 100644 src/EntityDb.Abstractions/States/IState.cs create mode 100644 src/EntityDb.Abstractions/States/IStateRepository.cs create mode 100644 src/EntityDb.Abstractions/States/IStateRepositoryFactory.cs rename src/EntityDb.Abstractions/{Snapshots => States}/Transforms/IMutator.cs (88%) rename src/EntityDb.Abstractions/{Snapshots => States}/Transforms/IReducer.cs (90%) create mode 100644 src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs create mode 100644 src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs create mode 100644 src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs delete mode 100644 src/EntityDb.Common/EventStreams/EventStreamRepository.cs delete mode 100644 src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs create mode 100644 src/EntityDb.Common/Exceptions/DataDeserializationException.cs create mode 100644 src/EntityDb.Common/Exceptions/DataSerializationException.cs delete mode 100644 src/EntityDb.Common/Exceptions/DeserializeException.cs delete mode 100644 src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs create mode 100644 src/EntityDb.Common/Exceptions/ExistingEntityException.cs create mode 100644 src/EntityDb.Common/Exceptions/ExistingStreamException.cs rename src/EntityDb.Common/Exceptions/{CannotWriteInReadOnlyModeException.cs => ReadOnlyWriteException.cs} (77%) delete mode 100644 src/EntityDb.Common/Exceptions/SerializeException.cs delete mode 100644 src/EntityDb.Common/Exceptions/SnapshotPointerDoesNotExistException.cs create mode 100644 src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs create mode 100644 src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs create mode 100644 src/EntityDb.Common/Exceptions/UnknownStreamException.cs delete mode 100644 src/EntityDb.Common/Extensions/SnapshotRepositoryFactoryExtensions.cs delete mode 100644 src/EntityDb.Common/Snapshots/SnapshotRepositoryWrapper.cs delete mode 100644 src/EntityDb.Common/Snapshots/TestModeSnapshotManager.cs delete mode 100644 src/EntityDb.Common/Snapshots/TestModeSnapshotRepository.cs delete mode 100644 src/EntityDb.Common/Snapshots/TestModeSnapshotRepositoryFactory.cs create mode 100644 src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs delete mode 100644 src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs delete mode 100644 src/EntityDb.Common/Sources/Attributes/Tag.cs rename src/EntityDb.Common/Sources/Documents/{IMessageDocument.cs => IMessageDataDocument.cs} (53%) rename src/EntityDb.Common/Sources/Documents/{IMessageGroupDocument.cs => ISourceDataDocument.cs} (53%) rename src/EntityDb.Common/Sources/Processors/{EntitySnapshotSourceProcessor.cs => EntityStateSourceProcessor.cs} (63%) delete mode 100644 src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs create mode 100644 src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs rename src/EntityDb.Common/{Extensions => Sources/Processors/Queues}/SourceProcessorQueueExtensions.cs (87%) create mode 100644 src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs delete mode 100644 src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs delete mode 100644 src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs delete mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs delete mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs create mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs rename src/EntityDb.Common/{Extensions => Sources/Queries/SortBuilders}/SortBuilderExtensions.cs (61%) delete mode 100644 src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs create mode 100644 src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs rename src/EntityDb.Common/{Extensions => Sources}/SourceRepositoryExtensions.cs (88%) rename src/EntityDb.Common/Sources/Subscribers/{EntitySnapshotSourceSubscriber.cs => EntityStateSourceSubscriber.cs} (66%) rename src/EntityDb.Common/Sources/Subscribers/{ProjectionSnapshotSourceSubscriber.cs => ProjectionStateSourceSubscriber.cs} (58%) rename src/EntityDb.Common/{Sources => States}/Attributes/Lease.cs (52%) create mode 100644 src/EntityDb.Common/States/Attributes/Tag.cs create mode 100644 src/EntityDb.Common/States/StateRepositoryFactoryExtensions.cs create mode 100644 src/EntityDb.Common/States/StateRepositoryWrapper.cs create mode 100644 src/EntityDb.Common/States/TestModeStateManager.cs create mode 100644 src/EntityDb.Common/States/TestModeStateRepository.cs create mode 100644 src/EntityDb.Common/States/TestModeStateRepositoryFactory.cs rename src/EntityDb.Common/{Snapshots/TryCatchSnapshotRepository.cs => States/TryCatchStateRepository.cs} (53%) create mode 100644 src/EntityDb.Common/Streams/MultipleStreamRepository.cs create mode 100644 src/EntityDb.Common/Streams/SingleEntityRepository.cs create mode 100644 src/EntityDb.Common/Streams/Stream.cs create mode 100644 src/EntityDb.Common/Streams/StreamRepositoryFactory.cs delete mode 100644 src/EntityDb.EntityFramework/Converters/IdConverter.cs delete mode 100644 src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs delete mode 100644 src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs delete mode 100644 src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs delete mode 100644 src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs delete mode 100644 src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs delete mode 100644 src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs delete mode 100644 src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj delete mode 100644 src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs delete mode 100644 src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs delete mode 100644 src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs delete mode 100644 src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs delete mode 100644 src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs delete mode 100644 src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs delete mode 100644 src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs delete mode 100644 src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs delete mode 100644 src/EntityDb.EntityFramework/packages.lock.json delete mode 100644 src/EntityDb.InMemory/EntityDb.InMemory.csproj delete mode 100644 src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs delete mode 100644 src/EntityDb.InMemory/Sessions/IInMemorySession.cs delete mode 100644 src/EntityDb.InMemory/Sessions/InMemorySession.cs delete mode 100644 src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs delete mode 100644 src/EntityDb.InMemory/Sessions/ReadOnlyInMemorySession.cs delete mode 100644 src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs delete mode 100644 src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepositoryFactory.cs delete mode 100644 src/EntityDb.InMemory/packages.lock.json rename src/EntityDb.MongoDb/Documents/{DeltaDocument.cs => DeltaDataDocument.cs} (50%) rename src/EntityDb.MongoDb/Documents/{LeaseDocument.cs => LeaseDataDocument.cs} (59%) create mode 100644 src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs delete mode 100644 src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs delete mode 100644 src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs create mode 100644 src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs rename src/EntityDb.MongoDb/Documents/{SnapshotDocument.cs => StateDocument.cs} (67%) rename src/EntityDb.MongoDb/Documents/{TagDocument.cs => TagDataDocument.cs} (54%) delete mode 100644 src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs delete mode 100644 src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs delete mode 100644 src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs delete mode 100644 src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs delete mode 100644 src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs delete mode 100644 src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs rename src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/{LeaseFilterBuilder.cs => LeaseDataFilterBuilder.cs} (57%) create mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs delete mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs rename src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/{TagFilterBuilder.cs => TagDataFilterBuilder.cs} (57%) delete mode 100644 src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs create mode 100644 src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs rename src/EntityDb.MongoDb/{Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs => States/AutoProvisionMongoDbStateRepositoryFactory.cs} (63%) create mode 100644 src/EntityDb.MongoDb/States/IMongoDbStateRepositoryFactory.cs create mode 100644 src/EntityDb.MongoDb/States/MongoDbStateRepository.cs rename src/EntityDb.MongoDb/{Snapshots/MongoDbSnapshotRepositoryFactory.cs => States/MongoDbStateRepositoryFactory.cs} (52%) create mode 100644 src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryExtensions.cs create mode 100644 src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryWrapper.cs create mode 100644 src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs rename src/EntityDb.MongoDb/{Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs => States/Sessions/MongoDbStateSessionOptions.cs} (80%) rename src/EntityDb.MongoDb/{Snapshots => States}/Sessions/MongoSession.cs (80%) create mode 100644 src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs rename src/EntityDb.MongoDb/{Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs => States/TestModeMongoDbStateRepositoryFactory.cs} (66%) delete mode 100644 src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs delete mode 100644 src/EntityDb.Npgsql/EntityDb.Npgsql.csproj delete mode 100644 src/EntityDb.Npgsql/Extensions/NpgsqlConnectionExtensions.cs delete mode 100644 src/EntityDb.Npgsql/Extensions/NpgsqlTransactionRepositoryFactoryExtensions.cs delete mode 100644 src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs delete mode 100644 src/EntityDb.Npgsql/Properties/AssemblyInfo.cs delete mode 100644 src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs delete mode 100644 src/EntityDb.Npgsql/Transactions/AutoProvisionNpgsqlTransactionRepositoryFactory.cs delete mode 100644 src/EntityDb.Npgsql/Transactions/NpgsqlTransactionRepositoryFactory.cs delete mode 100644 src/EntityDb.Npgsql/packages.lock.json delete mode 100644 src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs delete mode 100644 src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs create mode 100644 src/EntityDb.Redis/States/RedisStateRepository.cs rename src/EntityDb.Redis/{Snapshots/RedisSnapshotRepositoryFactory.cs => States/RedisStateRepositoryFactory.cs} (56%) create mode 100644 src/EntityDb.Redis/States/Sessions/IRedisSession.cs rename src/EntityDb.Redis/{Snapshots => States}/Sessions/RedisSession.cs (70%) rename src/EntityDb.Redis/{Snapshots/Sessions/RedisSnapshotSessionOptions.cs => States/Sessions/RedisStateSessionOptions.cs} (70%) delete mode 100644 src/EntityDb.SqlDb/Commands/DeleteDocumentsCommand.cs delete mode 100644 src/EntityDb.SqlDb/Commands/InsertDocumentsCommand.cs delete mode 100644 src/EntityDb.SqlDb/Converters/ISqlConverter.cs delete mode 100644 src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs delete mode 100644 src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/DocumentBase.cs delete mode 100644 src/EntityDb.SqlDb/Documents/IDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/IEntitiesDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/IEntityDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/ITransactionDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs delete mode 100644 src/EntityDb.SqlDb/EntityDb.SqlDb.csproj delete mode 100644 src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs delete mode 100644 src/EntityDb.SqlDb/Extensions/SqlDbTransactionRepositoryFactoryExtensions.cs delete mode 100644 src/EntityDb.SqlDb/Properties/AssemblyInfo.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/AndFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/AnyInFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/EqFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/GteFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/IFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/InFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/LteFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/NotFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Filter/OrFilterDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Sort/AscSortDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Sort/CombineSortDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Sort/DescSortDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/Definitions/Sort/ISortDefinition.cs delete mode 100644 src/EntityDb.SqlDb/Queries/DocumentQuery.cs delete mode 100644 src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/FilterBuilders/CommandFilterBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs delete mode 100644 src/EntityDb.SqlDb/Queries/FilterBuilders/LeaseFilterBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/FilterBuilders/TagFilterBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/SortBuilders/CommandSortBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/SortBuilders/LeaseSortBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs delete mode 100644 src/EntityDb.SqlDb/Queries/SortBuilders/TagSortBuilder.cs delete mode 100644 src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs delete mode 100644 src/EntityDb.SqlDb/Sessions/SqlDbSession.cs delete mode 100644 src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs delete mode 100644 src/EntityDb.SqlDb/Sessions/TestModeSqlDbSession.cs delete mode 100644 src/EntityDb.SqlDb/Transactions/ISqlDbTransactionRepositoryFactory.cs delete mode 100644 src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs delete mode 100644 src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepositoryFactoryWrapper.cs delete mode 100644 src/EntityDb.SqlDb/Transactions/TestModeSqlDbTransactionRepositoryFactory.cs delete mode 100644 src/EntityDb.SqlDb/packages.lock.json delete mode 100644 src/EntityDb.Void/EntityDb.Void.csproj delete mode 100644 src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs delete mode 100644 src/EntityDb.Void/Transactions/VoidSourceRepository.cs delete mode 100644 src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs delete mode 100644 src/EntityDb.Void/packages.lock.json delete mode 100644 test/.DS_Store delete mode 100644 test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs rename test/EntityDb.Common.Tests/Implementations/{ => Entities}/Deltas/DoNothing.cs (70%) rename test/EntityDb.Common.Tests/Implementations/{ => Entities}/Deltas/StoreNumber.cs (54%) delete mode 100644 test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs delete mode 100644 test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs rename test/EntityDb.Common.Tests/Implementations/{ => Sources}/Queries/CountQuery.cs (81%) rename test/EntityDb.Common.Tests/Implementations/{ => Sources}/Queries/SourceIdQuery.cs (58%) rename test/EntityDb.Common.Tests/Implementations/{ => Sources}/Queries/SourceTimeStampQuery.cs (65%) create mode 100644 test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs create mode 100644 test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs rename test/EntityDb.Common.Tests/Implementations/{Leases => States/Attributes}/CountLease.cs (56%) rename test/EntityDb.Common.Tests/Implementations/{Tags => States/Attributes}/CountTag.cs (50%) create mode 100644 test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs delete mode 100644 test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs delete mode 100644 test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs rename test/EntityDb.Common.Tests/{ => Sources}/Agents/AgentAccessorTestsBase.cs (85%) rename test/EntityDb.Common.Tests/{ => Sources}/Agents/UnknownAgentAccessorTests.cs (90%) delete mode 100644 test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs create mode 100644 test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs create mode 100644 test/EntityDb.Common.Tests/States/StateTests.cs create mode 100644 test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs create mode 100644 test/EntityDb.Common.Tests/Streams/StreamTests.cs diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 944d50c0a0110923524862fea11ad60cb0ba2f35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK-EPw`6h3YXbvhv`gv4%;a>LzJb!=i+NLkk+Rbm5?0T+O}WU01HSv)l<6_if7 z=Lxo#*mLj%JPv&R=~&ky?vP+U((!kEe2yJ|PVAV7SZf%q64i-_LnW3R6bnSQOP!OF z@l=6K_0gjj^o)A61+?-nFd*0No?5Rqb+B^3)^CT}^cpR@)SzREx{25i6A>a_zAT&2 z#t$X1h?eMp5)u?r0Bq0^WvHd|S?u&mR+XMp7h`m2hkWW>i@v~Xbv4?dSBTuib8Q0` zJQaI*Eu)O57|{M~zwGPcsBn~>m>GTxPm(yxo6Ya6RIc2*UFE#SYj0z8{vb_6dH~NfdKzWXGGH0FaSWJkfG^#+ncE3l1}p=AVnFT>E>xmtu+XTs4ixGN z0Ia~R1h)J=lsT?J&tRbuEfA(tfjX6$D+bf)@Vh3?GgxTU>BP+CgPD<;xuGyMI{J52 zI5AJ7EiD6}d#f9Lo2|Jfv4vkX`U{woGprR}#{n36eL=O!o5S{J$pl_KK` njh87f)KQFBc@!^0mB8;x1LzqnG{OV1KLUydTUZAEDg!?O>kE&Y diff --git a/src/.editorconfig b/.editorconfig similarity index 99% rename from src/.editorconfig rename to .editorconfig index db62372b..c342ff50 100644 --- a/src/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ # Remove the line below if you want to inherit .editorconfig settings from higher directories -id = true +root = true # C# files [*.cs] diff --git a/Directory.Build.props b/Directory.Build.props index b9d8bfa6..7f02f44a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,9 @@ - net7.0 + net7.0;net8.0 enable enable + 12 diff --git a/EntityDb.sln b/EntityDb.sln index 6434dc1f..83c05a3c 100644 --- a/EntityDb.sln +++ b/EntityDb.sln @@ -35,6 +35,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props global.json = global.json + ICON.png = ICON.png + LICENSE.txt = LICENSE.txt + README.md = README.md + SECURITY.md = SECURITY.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Json", "src\EntityDb.Json\EntityDb.Json.csproj", "{4936FFE0-98E5-43A2-89C9-0415A13CAA9B}" diff --git a/LICENSE.txt b/LICENSE.txt index ff8a1263..6df370cd 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 - 2023 entitydb.io +Copyright (c) 2021 - 2024 entitydb.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0dc6d54e..f2d5b2e5 100644 --- a/README.md +++ b/README.md @@ -29,79 +29,103 @@ There are several core objects at the heart of this implementation. Encapsulatin repositories. 1. Source Repository - - Agents + - Agent Signatures - Deltas - Tags - Leases - - Aliases -2. Snapshot Repository - - Snapshots -3. Entity Repository - - Transaction Repository - - Optional: Snapshot Repository -4. Projection Repository - - Transaction Repository - - Optional: Snapshot Repository +2. State Repository + - States +3. Stream Repository +4. Entity Repository +5. Projection Repository -### Transactions +### Sources -A source represents an atomic operation on multiple entities. A source is committed atomically or not -at all. If some step in the source fails, the entire source should fail. +A source represents an atomic operation on one or more states. +If any message in the source cannot be committed, the entire source +cannot be committed. -### Agents +### Agent Signatures -An agent is an actor that can record sources. For example, if a source is initiated via an HTTP API, you +An agent is an actor that can record sources and interact with states. Whenever a source is committed, +the signature of the agent is recorded with it. + +For example, if a source is initiated via an HTTP API, you might use the `HttpContextAgent` - it's signature includes headers and connection information. -### Delta +### Deltas + +A delta represents a change in state. Literally anything can be a delta. -A delta represents a change to a single entity. Going back to the bank account example, +Going back to the bank account example, one delta could be `PerformDeposit` while another could be `PerformWithdrawl`. The things that you can do (commands), -as well as things that are done elsewhere (events), are delta. +as well as things that are done elsewhere (events), are deltas. ### Tags -A tag is a way to index entities by some piece of information. A tag can have a label and a value, both of which are -strings. Many accounts are typed, and you could represent this with a tag where `Label` is `Type` and `Value` +A tag is a way to index sources by some piece of information contained within. A tag can have a label and a value, both +of which are +strings. + +Many accounts are typed, and you could represent this with a tag where `Label` is `Type` and `Value` is `Savings` or `Checking`. You could then run a query to get the account id of all accounts where `Label` is `Type` -and `Value` is `Savings`. The number of savings accounts in the system would be the number of entity ids. +and `Value` is `Savings`. The number of savings accounts in the system would be the number of state ids. ### Leases -A lease is like a tag, except that it has a uniqueness constraint. Many banks have online portals, allowing bank members +A lease is like a tag, except that it has a uniqueness constraint. In addition to a label and value, leases also have +a scope. + +Many banks have online portals, allowing bank members to see their accounts on the internet. From the bank's perspective, all of the accounts should be tied to a member id, probably a guid. But the member will not want to remember nor lookup this id - they will want to use a username. What you can do in EntityDb is make a lease for members where the `Scope` is `Global`, the `Label` is `Username`, and the `Value` is whatever username the member wants to use. If an attempt to commit a source is made -that would -violate the uniqueness constraint, it will be rejected. (This is obnoxious behavior for the user, though, so the bank -should check before attempting to commit to see if the username is available and give immediate feedback to choose a +that would violate the uniqueness constraint, the commit will fail. (However, this is bad UX, so the bank +should check that the username is available before attempting to commit and give immediate feedback to choose a different username). -### Snapshots +### State -A snapshot is a stateful object at a given point in time. They always have an identifier and a version. -Together, the identifier and version called a pointer. You can request different versions of a given snapshot -by using different pointers! +A state is an object at a given point in time. Each state has a unique identifier, and across time each state +has a multiple versions. Together, the identifier and a single version are called a pointer, and you can refer to a +state +at a given point in time by using the associated pointer. -In the context of snapshots, the reserved version is reserved for pointing to the latest snapshot. -So if you want the latest version, you use a pointer with the exact id and the reserved version. -If you want a specific version, you can create pointer with the exact id and version you want. +The default version, `Version.Zero` is given special meaning in certain scenarios. When attempting to load a state, it +means +that the state does not exist. When attempting to modify a state, it means that the previous version of the state is +irrelevant. -The balance on your bank account is a snapshot. You can build that snapshot by summing all of the deposits and -withdrawls on your account. If you look at the bank statements, you will most likely see the snapshot of each bank +The balance on your bank account is a state. You can build that state by summing all of the deposits and +withdrawls on your account. If you look at the bank statements, you will most likely see the state of each bank account for that statement, along with all of the deposits, withdrawls, and interest. -### Entities +### Stream Repository + +A stream repository is specialized at producing sources when the order of messages +is out of the control of the application. + +A great use case for a stream is to capture events coming from 3rd party, or even from +a different domain of your own suite of applications. + +Sorry - I have not yet completed my thoughts of how to apply the banking metaphor here. + +### Entity Repository + +An entity repository is specialized at producing sources when the order of messages +is completely in the control of the application, and consuming sources to generate +a state that determines the validity of requests. + +A great use case for an entity is to capture commands. -An entity is conceptually an aggregate id inside of a bounded context, and it extends the concept of a snapshot. In the banking example, there are multiple entities. You have a membership at the bank. That's an entity. You probably have a checking account. That's an entity. And you might even have a savings account. That is also an entity! -Which bounded contexts these entiies live in is up to the business. +### Projection Repository -### Projections +A projection repository does not produce sources, it only consumes them. -A projection is an aggregate, but notably _not_ the aggregate id, and it too extends the concept of a snapshot. -In the banking example, one example of a projection could be your entire account balance. It can be anything, though! +In the banking example, one example of a projection could be your entire balance across all accounts. It can be +anything, though! You are not constrained in what data you want to use for your projection. diff --git a/SECURITY.md b/SECURITY.md index 69157203..bf2368d5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,11 +2,12 @@ ## Supported Versions -Version | Supported -|---|---| -\>= 8.0 | :white_check_mark: -< 8.0 | :x: + Version | Supported +|---------|--------------------| + \>= 8.0 | :white_check_mark: + < 8.0 | :x: ## Reporting a Vulnerability -Please report vulnerabilities [here](https://github.com/entitydb-io/EntityDb.NET/issues/new?assignees=&labels=&template=bug_report.md&title=[VULNERABILITY]%20) +Please report +vulnerabilities [here](https://github.com/entitydb-io/EntityDb.NET/issues/new?assignees=&labels=&template=bug_report.md&title=[VULNERABILITY]%20) diff --git a/global.json b/global.json index ade31dad..dc106cda 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.401", + "version": "8.0.100", "allowPrerelease": false, "rollForward": "disable" } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 86d7917d..e1cf0076 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -13,7 +13,7 @@ snupkg Chris Philips entitydb.io - 2021 - 2023 © entitydb.io + 2021 - 2024 © entitydb.io https://github.com/entitydb-io/entitydb git en diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs deleted file mode 100644 index b85614e0..00000000 --- a/src/EntityDb.Abstractions/Entities/Deltas/IAddLeasesDelta.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Sources.Attributes; - -namespace EntityDb.Abstractions.Entities.Deltas; - -/// -/// Represents a delta that adds leases. -/// -/// The type of the entity. -public interface IAddLeasesDelta - where TEntity : IEntity -{ - /// - /// Returns the leases that need to be added. - /// - /// The entity for which leases will be added. - /// The leases that need to be added. - IEnumerable GetLeases(TEntity entity); -} diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs deleted file mode 100644 index b57c32b6..00000000 --- a/src/EntityDb.Abstractions/Entities/Deltas/IAddTagsDelta.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Sources.Attributes; - -namespace EntityDb.Abstractions.Entities.Deltas; - -/// -/// Represents a delta that adds tags. -/// -/// The type of the entity -public interface IAddTagsDelta - where TEntity : IEntity -{ - /// - /// Returns the tags that need to be added. - /// - /// The entity for which tags will be added - /// The tags that need to be added. - IEnumerable GetTags(TEntity entity); -} diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs deleted file mode 100644 index 4e144943..00000000 --- a/src/EntityDb.Abstractions/Entities/Deltas/IDeleteLeasesDelta.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Sources.Attributes; - -namespace EntityDb.Abstractions.Entities.Deltas; - -/// -/// Represents a delta that deletes leases. -/// -/// The type of the entity -public interface IDeleteLeasesDelta - where TEntity : IEntity -{ - /// - /// Returns the leases that need to be deleted. - /// - /// The entity for which leases will be removed. - /// The leases that need to be deleted. - IEnumerable GetLeases(TEntity entity); -} diff --git a/src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs b/src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs deleted file mode 100644 index 5b91c15d..00000000 --- a/src/EntityDb.Abstractions/Entities/Deltas/IDeleteTagsDelta.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Sources.Attributes; - -namespace EntityDb.Abstractions.Entities.Deltas; - -/// -/// Represents a delta that deletes tags. -/// -/// The type of the entity. -public interface IDeleteTagsDelta - where TEntity : IEntity -{ - /// - /// Returns the tags that need to be deleted. - /// - /// The root for which tags will be deleted. - /// The tags that need to be deleted. - IEnumerable GetTags(TEntity entity); -} diff --git a/src/EntityDb.Abstractions/Entities/IEntity.cs b/src/EntityDb.Abstractions/Entities/IEntity.cs index d2a7e0bb..4811829c 100644 --- a/src/EntityDb.Abstractions/Entities/IEntity.cs +++ b/src/EntityDb.Abstractions/Entities/IEntity.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.Entities; @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Entities; /// Indicates the entity is compatible with several EntityDb.Common implementations. /// /// The type of the entity. -public interface IEntity : ISnapshot +public interface IEntity : IState { /// /// Returns true if is not expected to throw an exception. diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs index 9e0096a6..c642cfea 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs @@ -4,7 +4,7 @@ namespace EntityDb.Abstractions.Entities; /// /// Represents a type used to create instances of -/// and . +/// and . /// /// The type of entity. public interface IEntityRepositoryFactory @@ -16,7 +16,7 @@ public interface IEntityRepositoryFactory /// A id associated with a . /// The name of the agent signature options. /// The agent's use case for the source repository. - /// The agent's use case for the snapshot repository. + /// The agent's use case for the state repository. /// A cancellation token. /// A new instance of . Task> CreateSingleForNew @@ -24,10 +24,10 @@ Task> CreateSingleForNew Id entityId, string agentSignatureOptionsName, string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ); - + /// /// Create a new instance of /// for an existing entity. @@ -35,7 +35,7 @@ Task> CreateSingleForNew /// A pointer associated with a . /// The name of the agent signature options. /// The agent's use case for the source repository. - /// The agent's use case for the snapshot repository. + /// The agent's use case for the state repository. /// A cancellation token. /// A new instance of . Task> CreateSingleForExisting @@ -43,23 +43,23 @@ Task> CreateSingleForExisting Pointer entityPointer, string agentSignatureOptionsName, string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ); - + /// /// Create a new instance of /// /// The name of the agent signature options. /// The agent's use case for the source repository. - /// The agent's use case for the snapshot repository. + /// The agent's use case for the state repository. /// A cancellation token. /// A new instance of . Task> CreateMultiple ( string agentSignatureOptionsName, string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ); } diff --git a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs index 19366a31..ef7512b1 100644 --- a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs @@ -1,12 +1,12 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Entities; /// -/// Manages the sources and snapshots of multiple entities. +/// Manages the sources and states of multiple entities. /// /// The type of the entity. public interface IMultipleEntityRepository : IDisposableResource @@ -17,12 +17,12 @@ public interface IMultipleEntityRepository : IDisposableResource ISourceRepository SourceRepository { get; } /// - /// The backing snapshot repository (if snapshot is available). + /// The backing state repository (if state is available). /// - ISnapshotRepository? SnapshotRepository { get; } + IStateRepository? StateRepository { get; } /// - /// Start a new entity if a given entity id. + /// Start a new entity with a given entity id. /// /// A new id for the new entity. /// @@ -31,7 +31,7 @@ public interface IMultipleEntityRepository : IDisposableResource void Create(Id entityId); /// - /// Associate a with a given entity id. + /// Associate a with a given state id. /// /// A pointer associated with a . /// A cancellation token @@ -41,26 +41,25 @@ public interface IMultipleEntityRepository : IDisposableResource /// . /// Task Load(Pointer entityPointer, CancellationToken cancellationToken = default); - + /// - /// Returns the snapshot of a for a given . + /// Returns the state of a for a given . /// /// The id of the entity. - /// The snapshot of a for . + /// The state of a for . TEntity Get(Id entityId); /// - /// Adds a single delta to the source with a given entity id. + /// Adds a single delta to the source with a given state id. /// /// The id associated with the . /// The new delta that modifies the . void Append(Id entityId, object delta); - + /// /// Atomically commits a source. /// - /// A new id for the new source. /// A cancellation token. /// true if the commit succeeded, or false if the commit failed. - Task Commit(Id sourceId, CancellationToken cancellationToken = default); + Task Commit(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs index 64eaf78b..eb3910fc 100644 --- a/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs @@ -1,12 +1,12 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Entities; /// -/// Manages the snapshots and sources of a single entity. +/// Manages the sources and states of a single entity. /// /// The type of the entity. public interface ISingleEntityRepository : IDisposableResource @@ -17,19 +17,19 @@ public interface ISingleEntityRepository : IDisposableResource ISourceRepository SourceRepository { get; } /// - /// The backing snapshot repository (if snapshot is available). + /// The backing state repository (if state is available). /// - ISnapshotRepository? SnapshotRepository { get; } - + IStateRepository? StateRepository { get; } + /// - /// The pointer for the currently entity. + /// The pointer for the current entity. /// Pointer EntityPointer { get; } - + /// - /// Returns the snapshot of a . + /// Returns the state of a . /// - /// The snapshot of a . + /// The state of a . TEntity Get(); /// @@ -37,12 +37,11 @@ public interface ISingleEntityRepository : IDisposableResource /// /// The new delta that modifies the . void Append(object delta); - + /// /// Atomically commits a source. /// - /// A new id for the new source. /// A cancellation token. /// true if the commit succeeded, or false if the commit failed. - Task Commit(Id sourceId, CancellationToken cancellationToken = default); + Task Commit(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj b/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj index c1b3c486..96e7fa11 100644 --- a/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj +++ b/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj @@ -1,7 +1,7 @@  - EntityDb EventSourcing DDD CQRS + EntityDb EventSourcing EventStreaming DDD CQRS An abstraction layer for the EntityDb suite of packages. diff --git a/src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs b/src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs deleted file mode 100644 index 743036b6..00000000 --- a/src/EntityDb.Abstractions/EventStreams/IEventStreamRepository.cs +++ /dev/null @@ -1,39 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.EventStreams; - -/// -/// Encapsulates the source repository and manages -/// -public interface IEventStreamRepository : IDisposableResource -{ - /// - /// The backing source repository. - /// - ISourceRepository SourceRepository { get; } - - /// - /// Stages a single delta with a given stream id and event id. - /// - /// The key associated with the stream. - /// A key associated with the event. - /// The new delta that modifies the event stream. - /// A cancellation token. - /// Only false if the event key already exists. - /// - /// If a duplicate event key is received for a given stream key, the delta - /// will be skipped. - /// - Task Stage(Key streamKey, Key eventKey, object delta, CancellationToken cancellationToken = default); - - /// - /// Commits all stages deltas. - /// - /// A new id for the new source. - /// How many attempts should be made to commit the source? - /// A cancellation token - /// true if the commit succeeded, or false if the commit failed. - Task Commit(Id sourceId, byte maxAttempts = 3, CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs b/src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs deleted file mode 100644 index bcfa124d..00000000 --- a/src/EntityDb.Abstractions/EventStreams/IEventStreamRepositoryFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace EntityDb.Abstractions.EventStreams; - -/// -/// Represents a type used to create instances of . -/// -public interface IEventStreamRepositoryFactory -{ - /// - /// Creates a new instance of . - /// - /// The name of the agent signature options. - /// The name of the source session options. - /// A cancellation token. - /// A new instance of . - Task CreateRepository(string agentSignatureOptionsName, - string sourceSessionOptionsName, CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index e267123c..47c016b4 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Projections; @@ -9,7 +9,7 @@ namespace EntityDb.Abstractions.Projections; /// Provides basic functionality for the common implementation of projections. /// /// -public interface IProjection : ISnapshot +public interface IProjection : IState { /// /// Incorporates the source into the projection. @@ -18,7 +18,7 @@ public interface IProjection : ISnapshot void Mutate(Source source); /// - /// Returns a that finds sources that need to be passed to the reducer. + /// Returns a that finds sources that need to be passed to the reducer. /// /// A service provider for fetching repositories. /// A pointer to the desired projection state @@ -31,9 +31,10 @@ IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Poin CancellationToken cancellationToken); /// - /// Maps a source to a set of entity ids. May be empty if the source does not map to the projection. + /// Maps a source to a set of relevant state ids. May be empty if none of the messages in the source + /// are relevant. /// /// A source - /// The entity ids for the projections. - static abstract IEnumerable EnumerateEntityIds(Source source); + /// The state ids for the projections. + static abstract IEnumerable EnumerateRelevantStateIds(Source source); } diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index 2e809559..c9e20495 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -1,25 +1,25 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Projections; /// -/// Encapsulates the snapshot repository for a projection. +/// Encapsulates the state repository for a projection. /// /// The type of the projection. public interface IProjectionRepository : IDisposableResource { /// - /// The backing snapshot repository. + /// The backing state repository. /// - ISnapshotRepository? SnapshotRepository { get; } + IStateRepository? StateRepository { get; } /// - /// Returns the snapshot of a for a given . + /// Returns the state of a for a given . /// /// A pointer to the projection. /// A cancellation token. - /// The snapshot of a for . - Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default); + /// The state of a for . + Task Get(Pointer projectionPointer, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs index d09ee8fd..5f1c6147 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs @@ -9,9 +9,9 @@ public interface IProjectionRepositoryFactory /// /// Create a new instance of /// - /// The agent's use case for the snapshot repository. + /// The agent's use case for the state repository. /// A cancellation token. /// A new instance of . - Task> CreateRepository(string? snapshotSessionOptionsName = null, + Task> CreateRepository(string? stateSessionOptionsName = null, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Snapshots/ISnapshot.cs b/src/EntityDb.Abstractions/Snapshots/ISnapshot.cs deleted file mode 100644 index e76d0e2e..00000000 --- a/src/EntityDb.Abstractions/Snapshots/ISnapshot.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Snapshots; - -/// -/// Indicates that the snapshot is compatible with several EntityDb.Common implementations. -/// -/// The type of the snapshot. -public interface ISnapshot -{ - /// - /// Creates a new instance of a . - /// - /// The pointer of the snapshot. - /// A new instance of . - static abstract TSnapshot Construct(Pointer pointer); - - /// - /// Returns a pointer for the current state of the snapshot. - /// - /// A pointer for the current state of the snapshot - Pointer GetPointer(); - - /// - /// Indicates if this snapshot instance version should be recorded (independent of the latest snapshot). - /// - /// true if this snapshot instance should be recorded, or else false. - /// - /// You would use this if you intent to fetch a snapshot at multiple versions and don't want to hit - /// the source database when it can be avoided. - /// - bool ShouldRecord(); - - /// - /// Indicates if this snapshot instance should be recorded as the latest snapshot. - /// - /// The previous instance of the latest snapshot. - /// true if this snapshot instance should be recorded as the latest snapshot, or else false. - bool ShouldRecordAsLatest(TSnapshot? previousLatestSnapshot); -} diff --git a/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs b/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs deleted file mode 100644 index f039d562..00000000 --- a/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Snapshots; - -/// -/// Represents a collection of snapshots. -/// -/// The type of snapshot stored in the . -public interface ISnapshotRepository : IDisposableResource -{ - /// - /// Returns an exact version of snapshot of a or - /// default(). - /// - /// A pointer to a specific snapshot. - /// A cancellation token. - /// - /// An exact version of snapshot of a or - /// default(). - /// - Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default); - - /// - /// Inserts a snapshot. - /// - /// A pointer to a snapshot. - /// The snapshot. - /// A cancellation token. - /// true if the insert succeeded, or false if the insert failed. - Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken = default); - - /// - /// Deletes multiple snapshots. - /// - /// Pointers to specific snapshots. - /// A cancellation token. - /// true if the deletes all succeeded, or false if any deletes failed. - Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/Snapshots/ISnapshotRepositoryFactory.cs b/src/EntityDb.Abstractions/Snapshots/ISnapshotRepositoryFactory.cs deleted file mode 100644 index cb5ff674..00000000 --- a/src/EntityDb.Abstractions/Snapshots/ISnapshotRepositoryFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using EntityDb.Abstractions.Disposables; - -namespace EntityDb.Abstractions.Snapshots; - -/// -/// Represents a type used to create instances of -/// -/// The type of snapshot stored by the . -public interface ISnapshotRepositoryFactory : IDisposableResource -{ - /// - /// Create a new instance of - /// - /// The agent's use case for the repository. - /// A cancellation token. - /// A new instance of . - Task> CreateRepository(string snapshotSessionOptionsName, - CancellationToken cancellationToken = default); -} diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs similarity index 67% rename from src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs rename to src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs index d371d81d..742abcc3 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceGroupData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs @@ -3,10 +3,10 @@ namespace EntityDb.Abstractions.Sources.Annotations; /// -/// Annotated source group data +/// Annotated message-level data /// /// The type of the data -public interface IAnnotatedSourceGroupData +public interface IAnnotatedMessageData { /// /// The id of the source @@ -19,9 +19,9 @@ public interface IAnnotatedSourceGroupData TimeStamp SourceTimeStamp { get; } /// - /// The ids of the messages + /// The id of the message /// - Id[] MessageIds { get; } + Id MessageId { get; } /// /// The data @@ -29,7 +29,7 @@ public interface IAnnotatedSourceGroupData TData Data { get; } /// - /// The pointers of the entities + /// A pointer to the state /// - Pointer[] EntityPointers { get; } + Pointer StatePointer { get; } } diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs index a708b163..42fd4657 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs @@ -3,7 +3,7 @@ namespace EntityDb.Abstractions.Sources.Annotations; /// -/// Annotated source data +/// Annotated source-level data /// /// The type of the data public interface IAnnotatedSourceData @@ -19,9 +19,9 @@ public interface IAnnotatedSourceData TimeStamp SourceTimeStamp { get; } /// - /// The id of the message + /// The ids of the messages /// - Id MessageId { get; } + Id[] MessageIds { get; } /// /// The data @@ -29,7 +29,7 @@ public interface IAnnotatedSourceData TData Data { get; } /// - /// A pointer to the entity + /// The pointers of the entities /// - Pointer EntityPointer { get; } + Pointer[] StatePointers { get; } } diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs index 8089311b..9cec7a05 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States.Attributes; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Sources; @@ -14,19 +14,19 @@ public interface ISourceRepository : IDisposableResource /// /// Returns the source ids which are found by a message group query. /// - /// The message group query. + /// The source data query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(IMessageGroupQuery messageGroupQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a message query. /// - /// The message query. + /// The message data query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(IMessageQuery messageQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// @@ -48,57 +48,57 @@ IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, CancellationToken cancellationToken = default); /// - /// Returns the entity pointers which are found by a agentSignature query. + /// Returns the state pointers which are found by a agentSignature query. /// - /// The message group query. + /// The source data query. /// A cancellation token. - /// The entity pointers which are found by . - IAsyncEnumerable EnumerateEntityPointers(IMessageGroupQuery messageGroupQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// - /// Returns the entity pointers which are found by a message query. + /// Returns the state pointers which are found by a message query. /// - /// The message query. + /// The message data query. /// A cancellation token. - /// The entity pointers which are found by . - IAsyncEnumerable EnumerateEntityPointers(IMessageQuery messageQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// - /// Returns the entity pointers which are found by a lease query. + /// Returns the state pointers which are found by a lease query. /// /// The lease query. /// A cancellation token. - /// The entity pointers which are found by . - IAsyncEnumerable EnumerateEntityPointers(ILeaseQuery leaseQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default); /// - /// Returns the entity pointers which are found by a tag query. + /// Returns the state pointers which are found by a tag query. /// /// The tag query. /// A cancellation token. - /// The entity pointers which are found by . - IAsyncEnumerable EnumerateEntityPointers(ITagQuery tagQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ITagQuery tagQuery, CancellationToken cancellationToken = default); /// /// Returns the agentSignatures which are found by an message group query. /// - /// The message group query. + /// The source data query. /// A cancellation token. - /// The agent signatures which are found by . - IAsyncEnumerable EnumerateAgentSignatures(IMessageGroupQuery messageGroupQuery, + /// The agent signatures which are found by . + IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the deltas which are found by a message query. /// - /// The message query. + /// The message data query. /// A cancellation token. - /// The deltas which are found by . - IAsyncEnumerable EnumerateDeltas(IMessageQuery messageQuery, + /// The deltas which are found by . + IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// @@ -122,20 +122,20 @@ IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, /// /// Returns the annotated agent signatures which are found by a message group query. /// - /// The message group query. + /// The source data query. /// A cancellation token. - /// The annotated agent signatures which are found by . - IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - IMessageGroupQuery messageGroupQuery, + /// The annotated agent signatures which are found by . + IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the annotated deltas which are found by a message query. /// - /// The message query. + /// The message data query. /// A cancellation token. - /// The annotated deltas which are found by . - IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageQuery messageQuery, + /// The annotated deltas which are found by . + IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs b/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs index c2669ff3..fa8b1cd7 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceRepositoryFactory.cs @@ -13,6 +13,6 @@ public interface ISourceRepositoryFactory : IDisposableResource /// The agent's use case for the repository. /// A cancellation token. /// A new instance of . - Task CreateRepository(string sourceSessionOptionsName, + Task Create(string sourceSessionOptionsName, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index e9cc072f..c61de1eb 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -1,6 +1,5 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; using EntityDb.Abstractions.ValueObjects; -using System.Collections.Immutable; namespace EntityDb.Abstractions.Sources; @@ -15,9 +14,9 @@ public sealed record Message public required Id Id { get; init; } /// - /// A pointer to the entity + /// A pointer to the state /// - public required Pointer EntityPointer { get; init; } + public required Pointer StatePointer { get; init; } /// /// The data. @@ -27,20 +26,20 @@ public sealed record Message /// /// The leases to be added. /// - public ImmutableArray AddLeases { get; init; } = ImmutableArray.Empty; + public ILease[] AddLeases { get; init; } = Array.Empty(); /// /// The tags to be added. /// - public ImmutableArray AddTags { get; init; } = ImmutableArray.Empty; + public ITag[] AddTags { get; init; } = Array.Empty(); /// /// The tags to be deleted. /// - public ImmutableArray DeleteLeases { get; init; } = ImmutableArray.Empty; + public ILease[] DeleteLeases { get; init; } = Array.Empty(); /// /// The aliases to be added. /// - public ImmutableArray DeleteTags { get; init; } = ImmutableArray.Empty; + public ITag[] DeleteTags { get; init; } = Array.Empty(); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs similarity index 90% rename from src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs index cfc25b06..e872f502 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// Builds a for a lease query. /// /// The type of filter used by the repository. -public interface ILeaseFilterBuilder : IMessageFilterBuilder +public interface ILeaseDataFilterBuilder : IMessageDataFilterBuilder { /// /// Returns a that only includes leases whose is diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs similarity index 50% rename from src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs index dfb7589d..5a01e242 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs @@ -9,38 +9,38 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// deltas, facts, tags, and aliases /// /// The type of filter used by the repository. -public interface IMessageFilterBuilder : IFilterBuilder +public interface IMessageDataFilterBuilder : IFilterBuilder { /// - /// Returns a that only includes objects with an entity - /// id in a set of entity ids. + /// Returns a that only includes objects with a state + /// id in a set of state ids. /// - /// The entity ids. + /// The state ids. /// - /// Returns a that only includes objects with an entity - /// id in . + /// Returns a that only includes objects with a state + /// id in . /// - TFilter EntityIdIn(params Id[] entityIds); + TFilter StateIdIn(params Id[] stateIds); /// - /// Returns a that only includes objects with an entity - /// version greater than or equal to a specific entity version. + /// Returns a that only includes objects with an state + /// version greater than or equal to a specific state version. /// - /// The entity ids. + /// The state ids. /// - /// Returns a that only includes objects with an entity - /// version greater than or equal to . + /// Returns a that only includes objects with an state + /// version greater than or equal to . /// - TFilter EntityVersionGte(Version entityVersion); + TFilter StateVersionGte(Version stateVersion); /// - /// Returns a that only includes objects with an entity - /// version greater than or equal to a specific entity version. + /// Returns a that only includes objects with an state + /// version greater than or equal to a specific state version. /// - /// The entity ids. + /// The state ids. /// - /// Returns a that only includes objects with an entity - /// version less than or equal to . + /// Returns a that only includes objects with an state + /// version less than or equal to . /// - TFilter EntityVersionLte(Version entityVersion); + TFilter StateVersionLte(Version stateVersion); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs similarity index 62% rename from src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs index a364e644..25a7010a 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageGroupFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs @@ -8,16 +8,16 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// agent signatures /// /// The type of filter used by the repository. -public interface IMessageGroupFilterBuilder : IFilterBuilder +public interface ISourceDataFilterBuilder : IFilterBuilder { /// - /// Returns a that only includes objects with any entity - /// id in a set of entity ids. + /// Returns a that only includes objects with any state + /// id in a set of state ids. /// - /// The entity ids. + /// The state ids. /// - /// Returns a that only includes objects with any entity - /// id in . + /// Returns a that only includes objects with any state + /// id in . /// - TFilter AnyEntityIdIn(params Id[] entityIds); + TFilter AnyStateIdIn(params Id[] stateIds); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs similarity index 87% rename from src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs index d05ea254..f334876b 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// Builds a for a tag query. /// /// The type of filter used by the repository. -public interface ITagFilterBuilder : IMessageFilterBuilder +public interface ITagDataFilterBuilder : IMessageDataFilterBuilder { /// /// Returns a that only includes tags whose is diff --git a/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs index 211de809..c0805d72 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs @@ -14,7 +14,7 @@ public interface ILeaseQuery : IQuery /// The type of filter used by the repository. /// The lease filter builder. /// A built from . - TFilter GetFilter(ILeaseFilterBuilder builder); + TFilter GetFilter(ILeaseDataFilterBuilder builder); /// /// Returns a built from a lease sort builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs similarity index 82% rename from src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs index 898d4b3c..3002f6cc 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/IMessageQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on messages. /// -public interface IMessageQuery : IQuery +public interface IMessageDataQuery : IQuery { /// /// Returns a built from a message filter builder. @@ -14,7 +14,7 @@ public interface IMessageQuery : IQuery /// The type of filter used by the repository. /// The message filter builder. /// A built from . - TFilter GetFilter(IMessageFilterBuilder builder); + TFilter GetFilter(IMessageDataFilterBuilder builder); /// /// Returns a built from a message sort builder. @@ -22,5 +22,5 @@ public interface IMessageQuery : IQuery /// The type of sort used by the repository. /// The message sort builder. /// A built from . - TSort? GetSort(IMessageSortBuilder builder); + TSort? GetSort(IMessageDataSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/IMessageGroupQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs similarity index 82% rename from src/EntityDb.Abstractions/Sources/Queries/IMessageGroupQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs index 4e677774..397dab7f 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/IMessageGroupQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on agentSignatures. /// -public interface IMessageGroupQuery : IQuery +public interface ISourceDataQuery : IQuery { /// /// Returns a built from a agentSignature filter builder. @@ -14,7 +14,7 @@ public interface IMessageGroupQuery : IQuery /// The type of filter used by the repository. /// The agentSignature filter builder. /// A built from . - TFilter GetFilter(IMessageGroupFilterBuilder builder); + TFilter GetFilter(ISourceDataFilterBuilder builder); /// /// Returns a built from a agentSignature sort builder. @@ -22,5 +22,5 @@ public interface IMessageGroupQuery : IQuery /// The type of sort used by the repository. /// The agentSignature sort builder. /// A built from . - TSort? GetSort(IMessageGroupSortBuilder builder); + TSort? GetSort(ISourceDataSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs index ee8bd644..7cd5c5c4 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs @@ -14,7 +14,7 @@ public interface ITagQuery : IQuery /// The type of filter used by the repository. /// The tag filter builder. /// A built from . - TFilter GetFilter(ITagFilterBuilder builder); + TFilter GetFilter(ITagDataFilterBuilder builder); /// /// Returns a built from a tag sort builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs index e070a892..ba5b161e 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; /// Builds a sort for a lease query. /// /// The type of sort used by the repository. -public interface ILeaseSortBuilder : IMessageSortBuilder +public interface ILeaseSortBuilder : IMessageDataSortBuilder { /// /// Returns a that orders leases by . diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs similarity index 74% rename from src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs index 53673da4..8339dda1 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs @@ -4,19 +4,19 @@ /// Builds a for a message query. /// /// The type of sort used by the repository. -public interface IMessageSortBuilder : ISortBuilder +public interface IMessageDataSortBuilder : ISortBuilder { /// - /// Returns a that orders objects by entity id. + /// Returns a that orders objects by state id. /// /// Pass true for ascending order or false for descending order. - /// A that orders objects by entity id. - TSort EntityId(bool ascending); + /// A that orders objects by state id. + TSort StateId(bool ascending); /// - /// Returns a that orders objects by entity version. + /// Returns a that orders objects by state version. /// /// Pass true for ascending order or false for descending order. - /// A that orders objects by entity version. - TSort EntityVersion(bool ascending); + /// A that orders objects by state version. + TSort StateVersion(bool ascending); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs similarity index 75% rename from src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs index 4e25cab0..96cf9dda 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageGroupSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs @@ -4,12 +4,12 @@ /// Builds a for a agentSignature query. /// /// The type of sort used by the repository. -public interface IMessageGroupSortBuilder : ISortBuilder +public interface ISourceDataSortBuilder : ISortBuilder { /// - /// Returns a that orders objects by entity ids. + /// Returns a that orders objects by state ids. /// /// Pass true for ascending order or false for descending order. - /// A that orders objects by entity ids. - TSort EntityIds(bool ascending); + /// A that orders objects by state ids. + TSort StateIds(bool ascending); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs index 85fa7ff0..292defab 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; /// Builds a sort for a tag query. /// /// The type of sort used by the repository. -public interface ITagSortBuilder : IMessageSortBuilder +public interface ITagSortBuilder : IMessageDataSortBuilder { /// /// Returns a that orders tags by . diff --git a/src/EntityDb.Abstractions/Sources/Source.cs b/src/EntityDb.Abstractions/Sources/Source.cs index ff3eed94..9ccc2ef5 100644 --- a/src/EntityDb.Abstractions/Sources/Source.cs +++ b/src/EntityDb.Abstractions/Sources/Source.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.ValueObjects; -using System.Collections.Immutable; namespace EntityDb.Abstractions.Sources; @@ -26,5 +25,5 @@ public sealed record Source /// /// The messages of the source. /// - public required ImmutableArray Messages { get; init; } + public required Message[] Messages { get; init; } } diff --git a/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs b/src/EntityDb.Abstractions/States/Attributes/ILease.cs similarity index 80% rename from src/EntityDb.Abstractions/Sources/Attributes/ILease.cs rename to src/EntityDb.Abstractions/States/Attributes/ILease.cs index ac71c772..f3380a31 100644 --- a/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs +++ b/src/EntityDb.Abstractions/States/Attributes/ILease.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.Sources.Attributes; +namespace EntityDb.Abstractions.States.Attributes; /// /// Represents a single metadata property and the context in which the metadata property must be unique. @@ -7,8 +7,8 @@ /// The source repository is responsible for enforcing the uniqueness constraint. /// If a lease needs to be unique in a global context, a constant should be used as the for all /// instances of the lease. -/// If a lease does not need to be unique in a global context, the entity id (or some other id which is unique to the -/// entity) should be included in the for all instances of the lease. +/// If a lease does not need to be unique in a global context, the state id (or some other id which is unique to the +/// state) should be included in the for all instances of the lease. /// A lease may have additional properties, but they are not directly relevant to the uniqueness constraint. /// public interface ILease diff --git a/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs b/src/EntityDb.Abstractions/States/Attributes/ITag.cs similarity index 61% rename from src/EntityDb.Abstractions/Sources/Attributes/ITag.cs rename to src/EntityDb.Abstractions/States/Attributes/ITag.cs index 35a4faf7..859d1e10 100644 --- a/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs +++ b/src/EntityDb.Abstractions/States/Attributes/ITag.cs @@ -1,7 +1,7 @@ -namespace EntityDb.Abstractions.Sources.Attributes; +namespace EntityDb.Abstractions.States.Attributes; /// -/// Represents a single metadata property, which can be used to query the current state of an entity. +/// Represents a single metadata property. /// public interface ITag { diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs new file mode 100644 index 00000000..04595bfa --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.States.Attributes; + +namespace EntityDb.Abstractions.States.Deltas; + +/// +/// Represents a delta that adds leases. +/// +/// The type of the state. +public interface IAddLeasesDelta +{ + /// + /// Returns the leases that need to be added. + /// + /// The state for which leases will be added. + /// The leases that need to be added. + IEnumerable GetLeases(TState state); +} diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs new file mode 100644 index 00000000..13ddf4b8 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.States.Attributes; + +namespace EntityDb.Abstractions.States.Deltas; + +/// +/// Represents a delta that adds tags. +/// +/// The type of the state +public interface IAddTagsDelta +{ + /// + /// Returns the tags that need to be added. + /// + /// The state for which tags will be added + /// The tags that need to be added. + IEnumerable GetTags(TState state); +} diff --git a/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs new file mode 100644 index 00000000..db305b66 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.States.Attributes; + +namespace EntityDb.Abstractions.States.Deltas; + +/// +/// Represents a delta that deletes leases. +/// +/// The type of the state +public interface IDeleteLeasesDelta +{ + /// + /// Returns the leases that need to be deleted. + /// + /// The state for which leases will be removed. + /// The leases that need to be deleted. + IEnumerable GetLeases(TState state); +} diff --git a/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs new file mode 100644 index 00000000..e7ba4981 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.States.Attributes; + +namespace EntityDb.Abstractions.States.Deltas; + +/// +/// Represents a delta that deletes tags. +/// +/// The type of the state. +public interface IDeleteTagsDelta +{ + /// + /// Returns the tags that need to be deleted. + /// + /// The state for which tags will be deleted. + /// The tags that need to be deleted. + IEnumerable GetTags(TState state); +} diff --git a/src/EntityDb.Abstractions/States/IState.cs b/src/EntityDb.Abstractions/States/IState.cs new file mode 100644 index 00000000..3b5bbc28 --- /dev/null +++ b/src/EntityDb.Abstractions/States/IState.cs @@ -0,0 +1,40 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.States; + +/// +/// Indicates that the state is compatible with several EntityDb.Common implementations. +/// +/// The type of the state. +public interface IState +{ + /// + /// Creates a new instance of a . + /// + /// The pointer of the state. + /// A new instance of . + static abstract TState Construct(Pointer pointer); + + /// + /// Returns a pointer for the current state of the state. + /// + /// A pointer for the current state of the state + Pointer GetPointer(); + + /// + /// Indicates if this state instance version should be recorded (independent of the latest state). + /// + /// true if this state instance should be recorded, or else false. + /// + /// You would use this if you intent to fetch a state at multiple versions and don't want to hit + /// the source database when it can be avoided. + /// + bool ShouldRecord(); + + /// + /// Indicates if this state instance should be recorded as the latest state. + /// + /// The previous instance of the latest state. + /// true if this state instance should be recorded as the latest state, or else false. + bool ShouldRecordAsLatest(TState? previousLatestState); +} diff --git a/src/EntityDb.Abstractions/States/IStateRepository.cs b/src/EntityDb.Abstractions/States/IStateRepository.cs new file mode 100644 index 00000000..ef886eda --- /dev/null +++ b/src/EntityDb.Abstractions/States/IStateRepository.cs @@ -0,0 +1,40 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.States; + +/// +/// Represents a collection of states. +/// +/// The type of state stored in the . +public interface IStateRepository : IDisposableResource +{ + /// + /// Returns an exact version of state of a or + /// default(). + /// + /// A pointer to a specific state. + /// A cancellation token. + /// + /// An exact version of state of a or + /// default(). + /// + Task Get(Pointer statePointer, CancellationToken cancellationToken = default); + + /// + /// Inserts a state. + /// + /// A pointer to a state. + /// The state. + /// A cancellation token. + /// true if the insert succeeded, or false if the insert failed. + Task Put(Pointer statePointer, TState state, CancellationToken cancellationToken = default); + + /// + /// Deletes multiple states. + /// + /// Pointers to specific states. + /// A cancellation token. + /// true if the deletes all succeeded, or false if any deletes failed. + Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/States/IStateRepositoryFactory.cs b/src/EntityDb.Abstractions/States/IStateRepositoryFactory.cs new file mode 100644 index 00000000..84776a3f --- /dev/null +++ b/src/EntityDb.Abstractions/States/IStateRepositoryFactory.cs @@ -0,0 +1,19 @@ +using EntityDb.Abstractions.Disposables; + +namespace EntityDb.Abstractions.States; + +/// +/// Represents a type used to create instances of +/// +/// The type of state stored by the . +public interface IStateRepositoryFactory : IDisposableResource +{ + /// + /// Create a new instance of + /// + /// The agent's use case for the repository. + /// A cancellation token. + /// A new instance of . + Task> Create(string stateSessionOptionsName, + CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Snapshots/Transforms/IMutator.cs b/src/EntityDb.Abstractions/States/Transforms/IMutator.cs similarity index 88% rename from src/EntityDb.Abstractions/Snapshots/Transforms/IMutator.cs rename to src/EntityDb.Abstractions/States/Transforms/IMutator.cs index 148fba22..abb790ca 100644 --- a/src/EntityDb.Abstractions/Snapshots/Transforms/IMutator.cs +++ b/src/EntityDb.Abstractions/States/Transforms/IMutator.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.Snapshots.Transforms; +namespace EntityDb.Abstractions.States.Transforms; /// /// Represents a type that can mutate one state into another state. diff --git a/src/EntityDb.Abstractions/Snapshots/Transforms/IReducer.cs b/src/EntityDb.Abstractions/States/Transforms/IReducer.cs similarity index 90% rename from src/EntityDb.Abstractions/Snapshots/Transforms/IReducer.cs rename to src/EntityDb.Abstractions/States/Transforms/IReducer.cs index 3edb7eb9..0656ec9d 100644 --- a/src/EntityDb.Abstractions/Snapshots/Transforms/IReducer.cs +++ b/src/EntityDb.Abstractions/States/Transforms/IReducer.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.Snapshots.Transforms; +namespace EntityDb.Abstractions.States.Transforms; /// /// Represents a type that can reduce one state into another state. diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs new file mode 100644 index 00000000..75d41d64 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -0,0 +1,41 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Streams; + +/// +/// Encapsulates the source repository and manages +/// +public interface IMultipleStreamRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// Load a stream if it exists, or create a new stream. + /// + /// A key associated with the stream. + /// A cancellation token + /// A task. + Task LoadOrCreate(Key streamKey, CancellationToken cancellationToken = default); + + /// + /// Stages a single delta with a given state key and message key. + /// + /// The key associated with the stream. + /// The key associated with the message. + /// The new delta that modifies the stream. + /// A cancellation token. + /// Only false if the message key already exists. + Task Stage(Key streamKey, Key messageKey, object delta, CancellationToken cancellationToken = default); + + /// + /// Commits all stages deltas. + /// + /// A cancellation token + /// true if the commit succeeded, or false if the commit failed. + Task Commit(CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs new file mode 100644 index 00000000..8be8471e --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs @@ -0,0 +1,37 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Streams; + +/// +/// Encapsulates the source repository and manages +/// +public interface ISingleStreamRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// The pointer for the current stream. + /// + Key StreamKey { get; } + + /// + /// Stages a single delta with a given state key and message key. + /// + /// The key associated with the message. + /// The new delta that modifies the stream. + /// A cancellation token. + /// Only false if the message key already exists. + Task Stage(Key messageKey, object delta, CancellationToken cancellationToken = default); + + /// + /// Commits all stages deltas. + /// + /// A cancellation token + /// true if the commit succeeded, or false if the commit failed. + Task Commit(CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs new file mode 100644 index 00000000..34b1efc1 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs @@ -0,0 +1,37 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Streams; + +/// +/// Represents a type used to create instances of . +/// +public interface IStreamRepositoryFactory +{ + /// + /// Create a new instance of + /// for an existing stream. + /// + /// A key associated with a stream. + /// The name of the agent signature options. + /// The agent's use case for the source repository. + /// A cancellation token. + /// A new instance of . + Task CreateSingle + ( + Key streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ); + + /// + /// Creates a new instance of . + /// + /// The name of the agent signature options. + /// The name of the source session options. + /// A cancellation token. + /// A new instance of . + Task CreateMultiple(string agentSignatureOptionsName, + string sourceSessionOptionsName, CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/ValueObjects/Id.cs b/src/EntityDb.Abstractions/ValueObjects/Id.cs index 6fab313f..e221efd5 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Id.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Id.cs @@ -1,7 +1,7 @@ namespace EntityDb.Abstractions.ValueObjects; /// -/// Represents an identifier for an entity or projection. +/// Represents an identifier for a state. /// /// The backing value. public readonly record struct Id(Guid Value) diff --git a/src/EntityDb.Abstractions/ValueObjects/Key.cs b/src/EntityDb.Abstractions/ValueObjects/Key.cs index f68315f0..a9a26fdf 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Key.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Key.cs @@ -1,7 +1,7 @@ namespace EntityDb.Abstractions.ValueObjects; /// -/// Represents a key for an event stream. +/// Represents a key for a stream. /// /// The backing value. public readonly record struct Key(string Value) diff --git a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs b/src/EntityDb.Abstractions/ValueObjects/Pointer.cs index 0d0ce9da..4b4619db 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Pointer.cs @@ -1,10 +1,10 @@ namespace EntityDb.Abstractions.ValueObjects; /// -/// Points to an entity or projection +/// References a state with a given id at a given version /// -/// The id of the entity or projection. -/// The version of the entity or projection. +/// The id of the state. +/// The version of the state. public readonly record struct Pointer(Id Id, Version Version) { /// diff --git a/src/EntityDb.Abstractions/ValueObjects/Version.cs b/src/EntityDb.Abstractions/ValueObjects/Version.cs index d15e34d3..240c327c 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Version.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Version.cs @@ -1,10 +1,10 @@ -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.ValueObjects; /// -/// Represents a version for an entity or projection. +/// Represents a version for an state. /// /// The backing value. public readonly record struct Version(ulong Value) @@ -13,8 +13,8 @@ public readonly record struct Version(ulong Value) /// This constant represents the minimum possible version. /// In the context of an , /// this value is reserved for auto-increment behavior. - /// In the context of an , - /// this value is reserved to point to the latest snapshot. + /// In the context of an , + /// this value is reserved to point to the latest state. /// public static readonly Version Zero = new(ulong.MinValue); diff --git a/src/EntityDb.Abstractions/packages.lock.json b/src/EntityDb.Abstractions/packages.lock.json index 6dbf1d3b..8317f497 100644 --- a/src/EntityDb.Abstractions/packages.lock.json +++ b/src/EntityDb.Abstractions/packages.lock.json @@ -16,6 +16,22 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" } + }, + "net8.0": { + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + } } } } \ No newline at end of file diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index 89b3ec16..2b2628b2 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Entities; @@ -9,23 +9,23 @@ namespace EntityDb.Common.Entities; internal class EntityRepositoryFactory : IEntityRepositoryFactory where TEntity : IEntity { - private readonly IServiceProvider _serviceProvider; private readonly IAgentAccessor _agentAccessor; - private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory; + private readonly IServiceProvider _serviceProvider; private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + private readonly IStateRepositoryFactory? _stateRepositoryFactory; public EntityRepositoryFactory ( IServiceProvider serviceProvider, IAgentAccessor agentAccessor, ISourceRepositoryFactory sourceRepositoryFactory, - ISnapshotRepositoryFactory? snapshotRepositoryFactory = null + IStateRepositoryFactory? stateRepositoryFactory = null ) { _serviceProvider = serviceProvider; _agentAccessor = agentAccessor; _sourceRepositoryFactory = sourceRepositoryFactory; - _snapshotRepositoryFactory = snapshotRepositoryFactory; + _stateRepositoryFactory = stateRepositoryFactory; } public async Task> CreateSingleForNew @@ -33,49 +33,49 @@ public async Task> CreateSingleForNew Id entityId, string agentSignatureOptionsName, string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ) { var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, - snapshotSessionOptionsName, cancellationToken); + stateSessionOptionsName, cancellationToken); multipleEntityRepository.Create(entityId); return new SingleEntityRepository(multipleEntityRepository, entityId); } - + public async Task> CreateSingleForExisting ( Pointer entityPointer, string agentSignatureOptionsName, string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ) { var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, - snapshotSessionOptionsName, cancellationToken); + stateSessionOptionsName, cancellationToken); await multipleEntityRepository.Load(entityPointer, cancellationToken); - + return new SingleEntityRepository(multipleEntityRepository, entityPointer); } - + public async Task> CreateMultiple ( string agentSignatureOptionsName, string sourceSessionOptionsName, - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ) { var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); - + var sourceRepository = await _sourceRepositoryFactory - .CreateRepository(sourceSessionOptionsName, cancellationToken); + .Create(sourceSessionOptionsName, cancellationToken); - if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) + if (_stateRepositoryFactory is null || stateSessionOptionsName is null) { return MultipleEntityRepository.Create ( @@ -85,15 +85,15 @@ public async Task> CreateMultiple ); } - var snapshotRepository = await _snapshotRepositoryFactory - .CreateRepository(snapshotSessionOptionsName, cancellationToken); + var stateRepository = await _stateRepositoryFactory + .Create(stateSessionOptionsName, cancellationToken); return MultipleEntityRepository.Create ( _serviceProvider, agent, sourceRepository, - snapshotRepository + stateRepository ); } } diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index 90711e6d..23cedcd2 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -1,15 +1,14 @@ using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Queries.Standard; using Microsoft.Extensions.DependencyInjection; -using System.Collections.Immutable; namespace EntityDb.Common.Entities; @@ -24,57 +23,57 @@ public MultipleEntityRepository ( IAgent agent, ISourceRepository sourceRepository, - ISnapshotRepository? snapshotRepository = null + IStateRepository? stateRepository = null ) { _agent = agent; SourceRepository = sourceRepository; - SnapshotRepository = snapshotRepository; + StateRepository = stateRepository; } public ISourceRepository SourceRepository { get; } - public ISnapshotRepository? SnapshotRepository { get; } + public IStateRepository? StateRepository { get; } public void Create(Id entityId) { if (_knownEntities.ContainsKey(entityId)) { - throw new EntityAlreadyLoadedException(); + throw new ExistingEntityException(); } var entity = TEntity.Construct(entityId); _knownEntities.Add(entityId, entity); } - + public async Task Load(Pointer entityPointer, CancellationToken cancellationToken = default) { if (_knownEntities.ContainsKey(entityPointer.Id)) { - throw new EntityAlreadyLoadedException(); + throw new ExistingEntityException(); } - - var snapshot = SnapshotRepository is not null - ? await SnapshotRepository.GetSnapshotOrDefault(entityPointer, cancellationToken) ?? + + var state = StateRepository is not null + ? await StateRepository.Get(entityPointer, cancellationToken) ?? TEntity.Construct(entityPointer.Id) : TEntity.Construct(entityPointer.Id); - var snapshotPointer = snapshot.GetPointer(); + var statePointer = state.GetPointer(); - var query = new GetDeltasQuery(entityPointer, snapshotPointer.Version); + var query = new GetDeltasQuery(entityPointer, statePointer.Version); var entity = await SourceRepository .EnumerateDeltas(query, cancellationToken) .AggregateAsync ( - snapshot, + state, (current, delta) => current.Reduce(delta), cancellationToken ); if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) { - throw new SnapshotPointerDoesNotExistException(); + throw new StateDoesNotExistException(); } _knownEntities.Add(entityPointer.Id, entity); @@ -84,17 +83,17 @@ public TEntity Get(Id entityId) { if (!_knownEntities.TryGetValue(entityId, out var entity)) { - throw new EntityNotLoadedException(); + throw new UnknownEntityIdException(); } - + return entity; } - + public void Append(Id entityId, object delta) { if (!_knownEntities.TryGetValue(entityId, out var entity)) { - throw new EntityNotLoadedException(); + throw new UnknownEntityIdException(); } entity = entity.Reduce(delta); @@ -102,48 +101,54 @@ public void Append(Id entityId, object delta) _messages.Add(new Message { Id = Id.NewId(), - EntityPointer = entity.GetPointer(), + StatePointer = entity.GetPointer(), Delta = delta, AddLeases = delta is IAddLeasesDelta addLeasesDelta - ? addLeasesDelta.GetLeases(entity).ToImmutableArray() - : ImmutableArray.Empty, + ? addLeasesDelta.GetLeases(entity).ToArray() + : Array.Empty(), AddTags = delta is IAddTagsDelta addTagsDelta - ? addTagsDelta.GetTags(entity).ToImmutableArray() - : ImmutableArray.Empty, + ? addTagsDelta.GetTags(entity).ToArray() + : Array.Empty(), DeleteLeases = delta is IDeleteLeasesDelta deleteLeasesDelta - ? deleteLeasesDelta.GetLeases(entity).ToImmutableArray() - : ImmutableArray.Empty, + ? deleteLeasesDelta.GetLeases(entity).ToArray() + : Array.Empty(), DeleteTags = delta is IDeleteTagsDelta deleteTagsDelta - ? deleteTagsDelta.GetTags(entity).ToImmutableArray() - : ImmutableArray.Empty, + ? deleteTagsDelta.GetTags(entity).ToArray() + : Array.Empty(), }); _knownEntities[entityId] = entity; } - - public async Task Commit(Id sourceId, - CancellationToken cancellationToken = default) + + public async Task Commit(CancellationToken cancellationToken = default) { var source = new Source { - Id = sourceId, + Id = Id.NewId(), TimeStamp = _agent.TimeStamp, AgentSignature = _agent.Signature, - Messages = _messages.ToImmutableArray(), + Messages = _messages.ToArray(), }; + var committed = await SourceRepository.Commit(source, cancellationToken); + + if (!committed) + { + return false; + } + _messages.Clear(); - - return await SourceRepository.Commit(source, cancellationToken); + + return true; } public override async ValueTask DisposeAsync() { await SourceRepository.DisposeAsync(); - if (SnapshotRepository is not null) + if (StateRepository is not null) { - await SnapshotRepository.DisposeAsync(); + await StateRepository.DisposeAsync(); } } @@ -152,10 +157,10 @@ public static MultipleEntityRepository Create IServiceProvider serviceProvider, IAgent agent, ISourceRepository sourceRepository, - ISnapshotRepository? snapshotRepository = null + IStateRepository? stateRepository = null ) { - if (snapshotRepository is null) + if (stateRepository is null) { return ActivatorUtilities.CreateInstance> ( @@ -167,10 +172,10 @@ public static MultipleEntityRepository Create return ActivatorUtilities.CreateInstance> ( - serviceProvider, + serviceProvider, agent, sourceRepository, - snapshotRepository + stateRepository ); } } diff --git a/src/EntityDb.Common/Entities/SingleEntityRepository.cs b/src/EntityDb.Common/Entities/SingleEntityRepository.cs index 89d5f60d..d4cae195 100644 --- a/src/EntityDb.Common/Entities/SingleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/SingleEntityRepository.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; @@ -14,12 +14,12 @@ internal sealed class SingleEntityRepository : DisposableResourceBaseCl public SingleEntityRepository(IMultipleEntityRepository multipleEntityRepository, Pointer entityPointer) { _multipleEntityRepository = multipleEntityRepository; - + EntityPointer = entityPointer; } public ISourceRepository SourceRepository => _multipleEntityRepository.SourceRepository; - public ISnapshotRepository? SnapshotRepository => _multipleEntityRepository.SnapshotRepository; + public IStateRepository? StateRepository => _multipleEntityRepository.StateRepository; public Pointer EntityPointer { get; } public TEntity Get() @@ -32,9 +32,9 @@ public void Append(object delta) _multipleEntityRepository.Append(EntityPointer.Id, delta); } - public Task Commit(Id sourceId, CancellationToken cancellationToken = default) + public Task Commit(CancellationToken cancellationToken = default) { - return _multipleEntityRepository.Commit(sourceId, cancellationToken); + return _multipleEntityRepository.Commit(cancellationToken); } public override ValueTask DisposeAsync() diff --git a/src/EntityDb.Common/EntityDb.Common.csproj b/src/EntityDb.Common/EntityDb.Common.csproj index 866acc40..e84fad44 100644 --- a/src/EntityDb.Common/EntityDb.Common.csproj +++ b/src/EntityDb.Common/EntityDb.Common.csproj @@ -1,7 +1,7 @@  - EntityDb EventSourcing DDD CQRS + EntityDb EventSourcing EventStreaming DDD CQRS A standard set of implementations (excluding persistence) of the EntityDb abstraction layer. diff --git a/src/EntityDb.Common/EventStreams/EventStreamRepository.cs b/src/EntityDb.Common/EventStreams/EventStreamRepository.cs deleted file mode 100644 index 78b66605..00000000 --- a/src/EntityDb.Common/EventStreams/EventStreamRepository.cs +++ /dev/null @@ -1,139 +0,0 @@ -using EntityDb.Abstractions.EventStreams; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Sources.Agents; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.Common.Sources.Attributes; -using EntityDb.Common.Sources.Queries.Standard; -using System.Collections.Immutable; - -namespace EntityDb.Common.EventStreams; - -internal sealed class EventStreamRepository : DisposableResourceBaseClass, IEventStreamRepository -{ - private const string RootScope = "EventStream"; - private const string StreamIdLabel = "StreamId"; - private const string EventIdLabel = "EventId"; - private readonly IAgent _agent; - private readonly Dictionary _knownEntityIds = new(); - private readonly List _messages = new(); - - public EventStreamRepository(IAgent agent, ISourceRepository sourceRepository) - { - _agent = agent; - - SourceRepository = sourceRepository; - } - - public ISourceRepository SourceRepository { get; } - - public async Task Stage(Key streamKey, Key eventKey, object delta, - CancellationToken cancellationToken = default) - { - var eventKeyLease = GetEventKeyLease(streamKey, eventKey); - - var entityPointer = await GetEntityPointer(eventKeyLease, cancellationToken); - - if (entityPointer != default) - { - return false; - } - - var addLeases = new List { eventKeyLease }; - - var (found, entityId) = await GetEntityId(streamKey, cancellationToken); - - if (!found) - { - addLeases.Add(GetStreamKeyLease(streamKey)); - } - - _messages.Add(new Message - { - Id = Id.NewId(), EntityPointer = entityId, Delta = delta, AddLeases = addLeases.ToImmutableArray(), - }); - - return true; - } - - public async Task Commit(Id sourceId, byte maxAttempts = 3, CancellationToken cancellationToken = default) - { - var source = new Source - { - Id = sourceId, - TimeStamp = _agent.TimeStamp, - AgentSignature = _agent.Signature, - Messages = _messages.ToImmutableArray(), - }; - - _messages.Clear(); - - byte attempt = 1; - - while (true) - { - var committed = await SourceRepository.Commit(source, cancellationToken); - - if (committed) - { - return committed; - } - - attempt += 1; - - if (attempt == maxAttempts) - { - break; - } - - await Task.Delay(100 * attempt, cancellationToken); - } - - return false; - } - - public override ValueTask DisposeAsync() - { - return SourceRepository.DisposeAsync(); - } - - public static ILease GetStreamKeyLease(Key streamKey) - { - return new Lease(RootScope, StreamIdLabel, streamKey.Value); - } - - public static ILease GetEventKeyLease(Key streamKey, Key eventKey) - { - return new Lease($"{RootScope}/{streamKey}", EventIdLabel, eventKey.Value); - } - - private async Task GetEntityPointer(ILease lease, CancellationToken cancellationToken) - { - var query = new MatchingLeaseQuery(lease); - - return await SourceRepository - .EnumerateEntityPointers(query, cancellationToken) - .SingleOrDefaultAsync(cancellationToken); - } - - private async Task<(bool, Id)> GetEntityId(Key streamKey, CancellationToken cancellationToken) - { - if (_knownEntityIds.TryGetValue(streamKey, out var entityId)) - { - return (true, entityId); - } - - var streamKeyLease = GetStreamKeyLease(streamKey); - - var entityPointer = await GetEntityPointer(streamKeyLease, cancellationToken); - - var found = entityPointer != default; - - entityId = found ? entityPointer.Id : Id.NewId(); - - _knownEntityIds.Add(streamKey, entityId); - - return (found, entityId); - } -} diff --git a/src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs b/src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs deleted file mode 100644 index 040abd29..00000000 --- a/src/EntityDb.Common/EventStreams/EventStreamRepositoryFactory.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.EventStreams; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Sources.Agents; -using Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Common.EventStreams; - -internal sealed class EventStreamRepositoryFactory : IEventStreamRepositoryFactory -{ - private readonly IAgentAccessor _agentAccessor; - private readonly IServiceProvider _serviceProvider; - private readonly ISourceRepositoryFactory _sourceRepositoryFactory; - - public EventStreamRepositoryFactory - ( - IServiceProvider serviceProvider, - IAgentAccessor agentAccessor, - ISourceRepositoryFactory sourceRepositoryFactory - ) - { - _serviceProvider = serviceProvider; - _agentAccessor = agentAccessor; - _sourceRepositoryFactory = sourceRepositoryFactory; - } - - public async Task CreateRepository - ( - string agentSignatureOptionsName, - string sourceSessionOptionsName, - CancellationToken cancellationToken - ) - { - var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); - - var sourceRepository = await _sourceRepositoryFactory - .CreateRepository(sourceSessionOptionsName, cancellationToken); - - return ActivatorUtilities.CreateInstance(_serviceProvider, agent, sourceRepository); - } -} diff --git a/src/EntityDb.Common/Exceptions/CannotResolveTypeException.cs b/src/EntityDb.Common/Exceptions/CannotResolveTypeException.cs index 477ca4b4..363adafa 100644 --- a/src/EntityDb.Common/Exceptions/CannotResolveTypeException.cs +++ b/src/EntityDb.Common/Exceptions/CannotResolveTypeException.cs @@ -5,6 +5,4 @@ namespace EntityDb.Common.Exceptions; /// /// The exception that is thrown when a cannot resolve a type. /// -public sealed class CannotResolveTypeException : Exception -{ -} +public sealed class CannotResolveTypeException : Exception; diff --git a/src/EntityDb.Common/Exceptions/DataDeserializationException.cs b/src/EntityDb.Common/Exceptions/DataDeserializationException.cs new file mode 100644 index 00000000..659ab7c5 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/DataDeserializationException.cs @@ -0,0 +1,6 @@ +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when data cannot be deserialized. +/// +public sealed class DataDeserializationException : Exception; diff --git a/src/EntityDb.Common/Exceptions/DataSerializationException.cs b/src/EntityDb.Common/Exceptions/DataSerializationException.cs new file mode 100644 index 00000000..efd0ee9b --- /dev/null +++ b/src/EntityDb.Common/Exceptions/DataSerializationException.cs @@ -0,0 +1,6 @@ +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when data cannot be serialized. +/// +public sealed class DataSerializationException : Exception; diff --git a/src/EntityDb.Common/Exceptions/DeserializeException.cs b/src/EntityDb.Common/Exceptions/DeserializeException.cs deleted file mode 100644 index a13480c0..00000000 --- a/src/EntityDb.Common/Exceptions/DeserializeException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an object envelope cannot be deserialized. Possible objects include: -/// agentSignatures, deltas, facts, leases, and aliases. -/// -public sealed class DeserializeException : Exception -{ -} diff --git a/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs b/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs deleted file mode 100644 index 34673f9a..00000000 --- a/src/EntityDb.Common/Exceptions/EntityAlreadyLoadedException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using EntityDb.Common.Entities; - -namespace EntityDb.Common.Exceptions; - -public sealed class EntityAlreadyLoadedException : Exception -{ -} - -public sealed class EntityNotLoadedException : Exception -{ -} diff --git a/src/EntityDb.Common/Exceptions/ExistingEntityException.cs b/src/EntityDb.Common/Exceptions/ExistingEntityException.cs new file mode 100644 index 00000000..183c1506 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/ExistingEntityException.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions.Entities; + +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when or +/// is called for an entity that already +/// exists in the repository. +/// +public sealed class ExistingEntityException : Exception; diff --git a/src/EntityDb.Common/Exceptions/ExistingStreamException.cs b/src/EntityDb.Common/Exceptions/ExistingStreamException.cs new file mode 100644 index 00000000..f67a66b9 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/ExistingStreamException.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.Streams; + +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when +/// is called for a stream that already exists in the repository. +/// +public sealed class ExistingStreamException : Exception; diff --git a/src/EntityDb.Common/Exceptions/NoAgentException.cs b/src/EntityDb.Common/Exceptions/NoAgentException.cs index 93d9c2fa..005048c2 100644 --- a/src/EntityDb.Common/Exceptions/NoAgentException.cs +++ b/src/EntityDb.Common/Exceptions/NoAgentException.cs @@ -6,6 +6,4 @@ namespace EntityDb.Common.Exceptions; /// The exception that is thrown when the cannot return an instance of /// . /// -public sealed class NoAgentException : Exception -{ -} +public sealed class NoAgentException : Exception; diff --git a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs index 68a0c143..ac9e9915 100644 --- a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs +++ b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs @@ -11,7 +11,7 @@ namespace EntityDb.Common.Exceptions; /// with any /// where the value of /// in -/// +/// /// is not equal to /// of the committed previous version. /// diff --git a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs b/src/EntityDb.Common/Exceptions/ReadOnlyWriteException.cs similarity index 77% rename from src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs rename to src/EntityDb.Common/Exceptions/ReadOnlyWriteException.cs index a19ab846..565293c5 100644 --- a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs +++ b/src/EntityDb.Common/Exceptions/ReadOnlyWriteException.cs @@ -6,6 +6,4 @@ namespace EntityDb.Common.Exceptions; /// The exception that is thrown when an actor passes a to an /// that was created for read-only mode. /// -public sealed class CannotWriteInReadOnlyModeException : Exception -{ -} +public sealed class ReadOnlyWriteException : Exception; diff --git a/src/EntityDb.Common/Exceptions/SerializeException.cs b/src/EntityDb.Common/Exceptions/SerializeException.cs deleted file mode 100644 index 9407aa6c..00000000 --- a/src/EntityDb.Common/Exceptions/SerializeException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an object envelope cannot be serialized. Possible objects include: -/// agentSignatures, deltas, leases, tags, and aliases. -/// -public sealed class SerializeException : Exception -{ -} diff --git a/src/EntityDb.Common/Exceptions/SnapshotPointerDoesNotExistException.cs b/src/EntityDb.Common/Exceptions/SnapshotPointerDoesNotExistException.cs deleted file mode 100644 index 134defd2..00000000 --- a/src/EntityDb.Common/Exceptions/SnapshotPointerDoesNotExistException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an actor requests a snapshot that does not exist. -/// -public sealed class SnapshotPointerDoesNotExistException : Exception -{ -} diff --git a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs new file mode 100644 index 00000000..f6fff059 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs @@ -0,0 +1,11 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Projections; + +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when , +/// , or +/// cannot find the requested state. +/// +public sealed class StateDoesNotExistException : Exception; diff --git a/src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs b/src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs new file mode 100644 index 00000000..2a729cb9 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions.Entities; + +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when or +/// is called for an entity that +/// is not loaded into the repository. +/// +public sealed class UnknownEntityIdException : Exception; diff --git a/src/EntityDb.Common/Exceptions/UnknownStreamException.cs b/src/EntityDb.Common/Exceptions/UnknownStreamException.cs new file mode 100644 index 00000000..7574982b --- /dev/null +++ b/src/EntityDb.Common/Exceptions/UnknownStreamException.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.Streams; + +namespace EntityDb.Common.Exceptions; + +/// +/// The exception that is thrown when +/// is called for an stream that is not loaded into the repository. +/// +public sealed class UnknownStreamException : Exception; diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index a41277a1..9f1c7438 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -1,15 +1,15 @@ using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.EventStreams; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Streams; using EntityDb.Common.Entities; -using EntityDb.Common.EventStreams; using EntityDb.Common.Projections; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; using EntityDb.Common.Sources.ReprocessorQueues; using EntityDb.Common.Sources.Subscribers; +using EntityDb.Common.Streams; using EntityDb.Common.TypeResolvers; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; @@ -150,37 +150,37 @@ public static void AddAgentAccessor(this IServiceCollection serv /// /// The service collection. /// The type of the entity. - public static void AddEntity(this IServiceCollection serviceCollection) + public static void AddEntityRepository(this IServiceCollection serviceCollection) where TEntity : IEntity { serviceCollection.AddTransient, EntityRepositoryFactory>(); } /// - /// Adds a source subscriber that records snapshots of entities. + /// Adds a source subscriber that records states of entities. /// /// The service collection. /// The agent's intent for the source repository. - /// The agent's intent for the snapshot repository. + /// The agent's intent for the state repository. /// The type of the entity. - public static void AddEntitySnapshotSourceSubscriber(this IServiceCollection serviceCollection, - string sourceSessionOptionsName, string snapshotSessionOptionsName) + public static void AddEntityStateSourceSubscriber(this IServiceCollection serviceCollection, + string sourceSessionOptionsName, string stateSessionOptionsName) where TEntity : IEntity { - serviceCollection.AddSingleton>(); + serviceCollection.AddSingleton>(); serviceCollection.AddScoped(serviceProvider => - EntitySnapshotSourceProcessor.Create(serviceProvider, sourceSessionOptionsName, - snapshotSessionOptionsName)); + EntityStateSourceProcessor.Create(serviceProvider, sourceSessionOptionsName, + stateSessionOptionsName)); } /// - /// Adds a transient implementation of + /// Adds a transient implementation of /// to a service collection. /// /// The service collection. - public static void AddEventStream(this IServiceCollection serviceCollection) + public static void AddStreamRepository(this IServiceCollection serviceCollection) { - serviceCollection.AddTransient(); + serviceCollection.AddTransient(); } /// @@ -188,7 +188,7 @@ public static void AddEventStream(this IServiceCollection serviceCollection) /// /// The service collection. /// The type of the projection. - public static void AddProjection( + public static void AddProjectionRepository( this IServiceCollection serviceCollection) where TProjection : IProjection { @@ -197,18 +197,18 @@ public static void AddProjection( } /// - /// Adds a source subscriber that records snapshots of projections. + /// Adds a source subscriber that records states of projections. /// /// The service collection. - /// The agent's intent for the snapshot repository. + /// The agent's intent for the state repository. /// The type of the projection. - public static void AddProjectionSnapshotSourceSubscriber( + public static void AddProjectionStateSourceSubscriber( this IServiceCollection serviceCollection, - string snapshotSessionOptionsName) + string stateSessionOptionsName) where TProjection : IProjection { - serviceCollection.AddSingleton>(); + serviceCollection.AddSingleton>(); serviceCollection.AddScoped(serviceProvider => - ProjectionSnapshotSourceProcessor.Create(serviceProvider, snapshotSessionOptionsName)); + ProjectionStateSourceProcessor.Create(serviceProvider, stateSessionOptionsName)); } } diff --git a/src/EntityDb.Common/Extensions/SnapshotRepositoryFactoryExtensions.cs b/src/EntityDb.Common/Extensions/SnapshotRepositoryFactoryExtensions.cs deleted file mode 100644 index 4266692c..00000000 --- a/src/EntityDb.Common/Extensions/SnapshotRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Snapshots; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.Common.Extensions; - -internal static class SnapshotRepositoryFactoryExtensions -{ - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static ISnapshotRepositoryFactory UseTestMode - ( - this ISnapshotRepositoryFactory snapshotRepositoryFactory, - bool testMode - ) - { - return testMode - ? new TestModeSnapshotRepositoryFactory(snapshotRepositoryFactory) - : snapshotRepositoryFactory; - } -} diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index 2bd630d3..78aa9389 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,5 +1,5 @@ using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; @@ -16,19 +16,19 @@ internal sealed class ProjectionRepository : DisposableResourceBase public ProjectionRepository ( IServiceProvider serviceProvider, - ISnapshotRepository? snapshotRepository = null + IStateRepository? stateRepository = null ) { _serviceProvider = serviceProvider; - SnapshotRepository = snapshotRepository; + StateRepository = stateRepository; } - public ISnapshotRepository? SnapshotRepository { get; } + public IStateRepository? StateRepository { get; } - public async Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default) + public async Task Get(Pointer projectionPointer, CancellationToken cancellationToken = default) { - var projection = SnapshotRepository is not null - ? await SnapshotRepository.GetSnapshotOrDefault(projectionPointer, cancellationToken) ?? + var projection = StateRepository is not null + ? await StateRepository.Get(projectionPointer, cancellationToken) ?? TProjection.Construct(projectionPointer.Id) : TProjection.Construct(projectionPointer.Id); @@ -41,21 +41,21 @@ public async Task GetSnapshot(Pointer projectionPointer, Cancellati if (!projectionPointer.IsSatisfiedBy(projection.GetPointer())) { - throw new SnapshotPointerDoesNotExistException(); + throw new StateDoesNotExistException(); } return projection; } public static IProjectionRepository Create(IServiceProvider serviceProvider, - ISnapshotRepository? snapshotRepository = null) + IStateRepository? stateRepository = null) { - if (snapshotRepository is null) + if (stateRepository is null) { return ActivatorUtilities.CreateInstance>(serviceProvider); } return ActivatorUtilities.CreateInstance>(serviceProvider, - snapshotRepository); + stateRepository); } } diff --git a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs index f15f69fe..a587fcdd 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs @@ -1,5 +1,5 @@ using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Projections; @@ -7,33 +7,33 @@ internal class ProjectionRepositoryFactory : IProjectionRepositoryF where TProjection : IProjection { private readonly IServiceProvider _serviceProvider; - private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory; + private readonly IStateRepositoryFactory? _stateRepositoryFactory; public ProjectionRepositoryFactory ( IServiceProvider serviceProvider, - ISnapshotRepositoryFactory? snapshotRepositoryFactory = null + IStateRepositoryFactory? stateRepositoryFactory = null ) { _serviceProvider = serviceProvider; - _snapshotRepositoryFactory = snapshotRepositoryFactory; + _stateRepositoryFactory = stateRepositoryFactory; } public async Task> CreateRepository ( - string? snapshotSessionOptionsName = null, + string? stateSessionOptionsName = null, CancellationToken cancellationToken = default ) { - if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) + if (_stateRepositoryFactory is null || stateSessionOptionsName is null) { return ProjectionRepository.Create(_serviceProvider); } - var snapshotRepository = - await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); + var stateRepository = + await _stateRepositoryFactory.Create(stateSessionOptionsName, cancellationToken); return ProjectionRepository.Create(_serviceProvider, - snapshotRepository); + stateRepository); } } diff --git a/src/EntityDb.Common/Snapshots/SnapshotRepositoryWrapper.cs b/src/EntityDb.Common/Snapshots/SnapshotRepositoryWrapper.cs deleted file mode 100644 index ffee245d..00000000 --- a/src/EntityDb.Common/Snapshots/SnapshotRepositoryWrapper.cs +++ /dev/null @@ -1,45 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; - -namespace EntityDb.Common.Snapshots; - -internal abstract class SnapshotRepositoryWrapper : DisposableResourceBaseClass, - ISnapshotRepository -{ - private readonly ISnapshotRepository _snapshotRepository; - - protected SnapshotRepositoryWrapper - ( - ISnapshotRepository snapshotRepository - ) - { - _snapshotRepository = snapshotRepository; - } - - public virtual Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, - CancellationToken cancellationToken = default) - { - return WrapCommand(() => _snapshotRepository.PutSnapshot(snapshotPointer, snapshot, cancellationToken)); - } - - public virtual Task GetSnapshotOrDefault(Pointer snapshotPointer, - CancellationToken cancellationToken = default) - { - return WrapQuery(() => _snapshotRepository.GetSnapshotOrDefault(snapshotPointer, cancellationToken)); - } - - public virtual Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) - { - return WrapCommand(() => _snapshotRepository.DeleteSnapshots(snapshotPointers, cancellationToken)); - } - - public override async ValueTask DisposeAsync() - { - await _snapshotRepository.DisposeAsync(); - } - - protected abstract Task WrapQuery(Func> task); - - protected abstract Task WrapCommand(Func> task); -} diff --git a/src/EntityDb.Common/Snapshots/TestModeSnapshotManager.cs b/src/EntityDb.Common/Snapshots/TestModeSnapshotManager.cs deleted file mode 100644 index af984dec..00000000 --- a/src/EntityDb.Common/Snapshots/TestModeSnapshotManager.cs +++ /dev/null @@ -1,55 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; - -namespace EntityDb.Common.Snapshots; - -internal class TestModeSnapshotManager : DisposableResourceBaseClass -{ - private readonly Dictionary, List> _dictionary = new(); - - private List GetStoredSnapshotPointers(ISnapshotRepository snapshotRepository) - { - if (_dictionary.TryGetValue(snapshotRepository, out var storedSnapshotPointers)) - { - return storedSnapshotPointers; - } - - storedSnapshotPointers = new List(); - - _dictionary.Add(snapshotRepository, storedSnapshotPointers); - - return storedSnapshotPointers; - } - - public void AddSnapshotPointer(ISnapshotRepository snapshotRepository, Pointer snapshotPointer) - { - var storedSnapshotPointers = GetStoredSnapshotPointers(snapshotRepository); - - storedSnapshotPointers.Add(snapshotPointer); - } - - public void RemoveSnapshotPointers(ISnapshotRepository snapshotRepository, - IEnumerable snapshotPointers) - { - var storedSnapshotPointers = GetStoredSnapshotPointers(snapshotRepository); - - storedSnapshotPointers.RemoveAll(snapshotPointers.Contains); - - if (storedSnapshotPointers.Count == 0) - { - _dictionary.Remove(snapshotRepository); - } - } - - /// - /// This should only be called by the snapshot repository factory. - /// - public override async ValueTask DisposeAsync() - { - foreach (var (snapshotRepository, storedSnapshotPointers) in _dictionary.ToArray()) - { - await snapshotRepository.DeleteSnapshots(storedSnapshotPointers.ToArray()); - } - } -} diff --git a/src/EntityDb.Common/Snapshots/TestModeSnapshotRepository.cs b/src/EntityDb.Common/Snapshots/TestModeSnapshotRepository.cs deleted file mode 100644 index 809b5e95..00000000 --- a/src/EntityDb.Common/Snapshots/TestModeSnapshotRepository.cs +++ /dev/null @@ -1,47 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; - -namespace EntityDb.Common.Snapshots; - -internal sealed class TestModeSnapshotRepository : DisposableResourceBaseClass, - ISnapshotRepository -{ - private readonly ISnapshotRepository _snapshotRepository; - private readonly TestModeSnapshotManager _testModeSnapshotManager; - - public TestModeSnapshotRepository - ( - ISnapshotRepository snapshotRepository, - TestModeSnapshotManager testModeSnapshotManager - ) - { - _snapshotRepository = snapshotRepository; - _testModeSnapshotManager = testModeSnapshotManager; - } - - public Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, - CancellationToken cancellationToken = default) - { - _testModeSnapshotManager.AddSnapshotPointer(this, snapshotPointer); - - return _snapshotRepository.PutSnapshot(snapshotPointer, snapshot, cancellationToken); - } - - public Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default) - { - return _snapshotRepository.GetSnapshotOrDefault(snapshotPointer, cancellationToken); - } - - public Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) - { - _testModeSnapshotManager.RemoveSnapshotPointers(this, snapshotPointers); - - return _snapshotRepository.DeleteSnapshots(snapshotPointers, cancellationToken); - } - - public override async ValueTask DisposeAsync() - { - await _snapshotRepository.DisposeAsync(); - } -} diff --git a/src/EntityDb.Common/Snapshots/TestModeSnapshotRepositoryFactory.cs b/src/EntityDb.Common/Snapshots/TestModeSnapshotRepositoryFactory.cs deleted file mode 100644 index 4fe3eb23..00000000 --- a/src/EntityDb.Common/Snapshots/TestModeSnapshotRepositoryFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Disposables; - -namespace EntityDb.Common.Snapshots; - -internal sealed class TestModeSnapshotRepositoryFactory : DisposableResourceBaseClass, - ISnapshotRepositoryFactory -{ - private readonly ISnapshotRepositoryFactory _snapshotRepositoryFactory; - private readonly TestModeSnapshotManager _testModeSnapshotManager = new(); - - public TestModeSnapshotRepositoryFactory - ( - ISnapshotRepositoryFactory snapshotRepositoryFactory - ) - { - _snapshotRepositoryFactory = snapshotRepositoryFactory; - } - - public async Task> CreateRepository(string snapshotSessionOptionsName, - CancellationToken cancellationToken = default) - { - var snapshotRepository = - await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); - - return new TestModeSnapshotRepository(snapshotRepository, _testModeSnapshotManager); - } - - public override async ValueTask DisposeAsync() - { - await _testModeSnapshotManager.DisposeAsync(); - await _snapshotRepositoryFactory.DisposeAsync(); - } -} diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs new file mode 100644 index 00000000..4a9cf278 --- /dev/null +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs @@ -0,0 +1,36 @@ +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Annotations; + +internal record AnnotatedMessageData +( + Id SourceId, + TimeStamp SourceTimeStamp, + Id MessageId, + TData Data, + Pointer StatePointer +) : IAnnotatedMessageData +{ + public static IAnnotatedMessageData CreateFromBoxedData + ( + Id sourceId, + TimeStamp sourceTimeStamp, + Id messageId, + object boxedData, + Pointer statePointer + ) + { + var dataAnnotationType = typeof(AnnotatedMessageData<>).MakeGenericType(boxedData.GetType()); + + return (IAnnotatedMessageData)Activator.CreateInstance + ( + dataAnnotationType, + sourceId, + sourceTimeStamp, + messageId, + boxedData, + statePointer + )!; + } +} diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs index 5fb373e6..e1dda1de 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs @@ -7,18 +7,18 @@ internal record AnnotatedSourceData ( Id SourceId, TimeStamp SourceTimeStamp, - Id MessageId, + Id[] MessageIds, TData Data, - Pointer EntityPointer + Pointer[] StatePointers ) : IAnnotatedSourceData { public static IAnnotatedSourceData CreateFromBoxedData ( Id sourceId, TimeStamp sourceTimeStamp, - Id messageId, + Id[] messageIds, object boxedData, - Pointer entityPointer + Pointer[] statePointers ) { var dataAnnotationType = typeof(AnnotatedSourceData<>).MakeGenericType(boxedData.GetType()); @@ -28,9 +28,9 @@ Pointer entityPointer dataAnnotationType, sourceId, sourceTimeStamp, - messageId, + messageIds, boxedData, - entityPointer + statePointers )!; } } diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs deleted file mode 100644 index 12d468a2..00000000 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceGroupData.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Sources.Annotations; - -internal record AnnotatedSourceGroupData -( - Id SourceId, - TimeStamp SourceTimeStamp, - Id[] MessageIds, - TData Data, - Pointer[] EntityPointers -) : IAnnotatedSourceGroupData -{ - public static IAnnotatedSourceGroupData CreateFromBoxedData - ( - Id sourceId, - TimeStamp sourceTimeStamp, - Id[] messageIds, - object boxedData, - Pointer[] entityPointers - ) - { - var dataAnnotationType = typeof(AnnotatedSourceGroupData<>).MakeGenericType(boxedData.GetType()); - - return (IAnnotatedSourceGroupData)Activator.CreateInstance - ( - dataAnnotationType, - sourceId, - sourceTimeStamp, - messageIds, - boxedData, - entityPointers - )!; - } -} diff --git a/src/EntityDb.Common/Sources/Attributes/Tag.cs b/src/EntityDb.Common/Sources/Attributes/Tag.cs deleted file mode 100644 index ab0905bf..00000000 --- a/src/EntityDb.Common/Sources/Attributes/Tag.cs +++ /dev/null @@ -1,6 +0,0 @@ -using EntityDb.Abstractions.Sources.Attributes; - -namespace EntityDb.Common.Sources.Attributes; - -/// -public sealed record Tag(string Label, string Value) : ITag; diff --git a/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs index 313d4531..077fff74 100644 --- a/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs +++ b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs @@ -58,14 +58,14 @@ Func, IAsyncEnumerable> mapToPointers return pointers; } - public static async IAsyncEnumerable> EnumerateEntityAnnotation> EnumerateAnnotatedSourceData ( this IAsyncEnumerable documents, IEnvelopeService envelopeService, [EnumeratorCancellation] CancellationToken cancellationToken ) - where TDocument : IMessageDocument + where TDocument : IMessageDataDocument where TData : notnull { await using var enumerator = documents.GetAsyncEnumerator(cancellationToken); @@ -74,25 +74,25 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var document = enumerator.Current; - yield return AnnotatedSourceData.CreateFromBoxedData + yield return AnnotatedMessageData.CreateFromBoxedData ( document.SourceId, document.SourceTimeStamp, document.MessageId, envelopeService.Deserialize(document.Data), - document.EntityPointer + document.StatePointer ); } } - public static async IAsyncEnumerable> EnumerateEntitiesAnnotation> EnumerateEntitiesAnnotation ( this IAsyncEnumerable documents, IEnvelopeService envelopeService, [EnumeratorCancellation] CancellationToken cancellationToken ) - where TDocument : IMessageGroupDocument + where TDocument : ISourceDataDocument where TData : notnull { await using var enumerator = documents.GetAsyncEnumerator(cancellationToken); @@ -101,13 +101,13 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var document = enumerator.Current; - yield return AnnotatedSourceGroupData.CreateFromBoxedData + yield return AnnotatedSourceData.CreateFromBoxedData ( document.SourceId, document.SourceTimeStamp, document.MessageIds, envelopeService.Deserialize(document.Data), - document.EntityPointers + document.StatePointers ); } } diff --git a/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs b/src/EntityDb.Common/Sources/Documents/IMessageDataDocument.cs similarity index 53% rename from src/EntityDb.Common/Sources/Documents/IMessageDocument.cs rename to src/EntityDb.Common/Sources/Documents/IMessageDataDocument.cs index e53b5bf0..93eb770e 100644 --- a/src/EntityDb.Common/Sources/Documents/IMessageDocument.cs +++ b/src/EntityDb.Common/Sources/Documents/IMessageDataDocument.cs @@ -2,8 +2,8 @@ namespace EntityDb.Common.Sources.Documents; -internal interface IMessageDocument : IDocument +internal interface IMessageDataDocument : IDocument { Id MessageId { get; } - Pointer EntityPointer { get; } + Pointer StatePointer { get; } } diff --git a/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs b/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs similarity index 53% rename from src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs rename to src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs index 4b4040a5..48fd6811 100644 --- a/src/EntityDb.Common/Sources/Documents/IMessageGroupDocument.cs +++ b/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs @@ -2,8 +2,8 @@ namespace EntityDb.Common.Sources.Documents; -internal interface IMessageGroupDocument : IDocument +internal interface ISourceDataDocument : IDocument { Id[] MessageIds { get; } - Pointer[] EntityPointers { get; } + Pointer[] StatePointers { get; } } diff --git a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs similarity index 63% rename from src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs rename to src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs index 1418ccde..ca75c64c 100644 --- a/src/EntityDb.Common/Sources/Processors/EntitySnapshotSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs @@ -7,30 +7,30 @@ namespace EntityDb.Common.Sources.Processors; /// -/// A source processor that creates entity snapshots +/// A source processor that persists entity states /// /// The type of the entity -public sealed class EntitySnapshotSourceProcessor : ISourceProcessor +public sealed class EntityStateSourceProcessor : ISourceProcessor where TEntity : IEntity { private readonly IEntityRepositoryFactory _entityRepositoryFactory; - private readonly ILogger> _logger; - private readonly string _snapshotSessionOptionsName; + private readonly ILogger> _logger; private readonly string _sourceSessionOptionsName; + private readonly string _stateSessionOptionsName; /// - public EntitySnapshotSourceProcessor + public EntityStateSourceProcessor ( - ILogger> logger, + ILogger> logger, IEntityRepositoryFactory entityRepositoryFactory, string sourceSessionOptionsName, - string snapshotSessionOptionsName + string stateSessionOptionsName ) { _logger = logger; _entityRepositoryFactory = entityRepositoryFactory; _sourceSessionOptionsName = sourceSessionOptionsName; - _snapshotSessionOptionsName = snapshotSessionOptionsName; + _stateSessionOptionsName = stateSessionOptionsName; } /// @@ -42,11 +42,11 @@ public async Task Process(Source source, CancellationToken cancellationToken) } await using var entityRepository = await _entityRepositoryFactory - .CreateMultiple(default!, _sourceSessionOptionsName, _snapshotSessionOptionsName, cancellationToken); + .CreateMultiple(default!, _sourceSessionOptionsName, _stateSessionOptionsName, cancellationToken); - if (entityRepository.SnapshotRepository is null) + if (entityRepository.StateRepository is null) { - _logger.LogWarning("Snapshots not enabled, no point in processing source."); + _logger.LogWarning("State repository not available, skipping source processing."); return; } @@ -56,12 +56,12 @@ public async Task Process(Source source, CancellationToken cancellationToken) foreach (var message in source.Messages) { - var entityPointer = message.EntityPointer; + var entityPointer = message.StatePointer; if (!latestEntities.Remove(entityPointer.Id, out var previousEntity)) { previousEntity = - await entityRepository.SnapshotRepository.GetSnapshotOrDefault(entityPointer, cancellationToken); + await entityRepository.StateRepository.Get(entityPointer, cancellationToken); } var nextEntity = (previousEntity ?? TEntity.Construct(entityPointer.Id)).Reduce(message.Delta); @@ -84,14 +84,14 @@ public async Task Process(Source source, CancellationToken cancellationToken) foreach (var (entityPointer, entity) in saveEntities) { - await entityRepository.SnapshotRepository.PutSnapshot(entityPointer, entity, cancellationToken); + await entityRepository.StateRepository.Put(entityPointer, entity, cancellationToken); } } - internal static EntitySnapshotSourceProcessor Create(IServiceProvider serviceProvider, - string sourceSessionOptionsName, string snapshotSessionOptionsName) + internal static EntityStateSourceProcessor Create(IServiceProvider serviceProvider, + string sourceSessionOptionsName, string stateSessionOptionsName) { - return ActivatorUtilities.CreateInstance>(serviceProvider, - sourceSessionOptionsName, snapshotSessionOptionsName); + return ActivatorUtilities.CreateInstance>(serviceProvider, + sourceSessionOptionsName, stateSessionOptionsName); } } diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs deleted file mode 100644 index 218cf3cc..00000000 --- a/src/EntityDb.Common/Sources/Processors/ProjectionSnapshotSourceProcessor.cs +++ /dev/null @@ -1,80 +0,0 @@ -using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Sources; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace EntityDb.Common.Sources.Processors; - -/// -/// A source processor that creates projection snapshots -/// -/// -public sealed class ProjectionSnapshotSourceProcessor : ISourceProcessor - where TProjection : IProjection -{ - private readonly ILogger> _logger; - private readonly IProjectionRepositoryFactory _projectionRepositoryFactory; - private readonly string _snapshotSessionOptionsName; - - /// - public ProjectionSnapshotSourceProcessor - ( - ILogger> logger, - IProjectionRepositoryFactory projectionRepositoryFactory, - string snapshotSessionOptionsName - ) - { - _logger = logger; - _projectionRepositoryFactory = projectionRepositoryFactory; - _snapshotSessionOptionsName = snapshotSessionOptionsName; - } - - /// - public async Task Process(Source source, CancellationToken cancellationToken) - { - await using var projectionRepository = await _projectionRepositoryFactory - .CreateRepository(_snapshotSessionOptionsName, cancellationToken); - - if (projectionRepository.SnapshotRepository is null) - { - _logger.LogWarning("Snapshots not enabled, no point in processing source."); - - return; - } - - foreach (var entityId in TProjection.EnumerateEntityIds(source)) - { - var previousProjection = await projectionRepository.SnapshotRepository - .GetSnapshotOrDefault(entityId, cancellationToken); - - var nextProjection = previousProjection == null - ? TProjection.Construct(entityId) - : previousProjection; - - nextProjection.Mutate(source); - - var nextProjectionPointer = nextProjection.GetPointer(); - - if (nextProjection.ShouldRecordAsLatest(previousProjection)) - { - await projectionRepository.SnapshotRepository.PutSnapshot(entityId, nextProjection, - cancellationToken); - } - - if (nextProjection.ShouldRecord()) - { - await projectionRepository.SnapshotRepository.PutSnapshot(nextProjectionPointer, nextProjection, - cancellationToken); - } - - cancellationToken.ThrowIfCancellationRequested(); - } - } - - internal static ProjectionSnapshotSourceProcessor Create(IServiceProvider serviceProvider, - string snapshotSessionOptionsName) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, - snapshotSessionOptionsName); - } -} diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs new file mode 100644 index 00000000..429f8625 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs @@ -0,0 +1,80 @@ +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Sources; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.Processors; + +/// +/// A source processor that persists projection states +/// +/// The type of the projection +public sealed class ProjectionStateSourceProcessor : ISourceProcessor + where TProjection : IProjection +{ + private readonly ILogger> _logger; + private readonly IProjectionRepositoryFactory _projectionRepositoryFactory; + private readonly string _stateSessionOptionsName; + + /// + public ProjectionStateSourceProcessor + ( + ILogger> logger, + IProjectionRepositoryFactory projectionRepositoryFactory, + string stateSessionOptionsName + ) + { + _logger = logger; + _projectionRepositoryFactory = projectionRepositoryFactory; + _stateSessionOptionsName = stateSessionOptionsName; + } + + /// + public async Task Process(Source source, CancellationToken cancellationToken) + { + await using var projectionRepository = await _projectionRepositoryFactory + .CreateRepository(_stateSessionOptionsName, cancellationToken); + + if (projectionRepository.StateRepository is null) + { + _logger.LogWarning("State repository not enabled, skipping source processing."); + + return; + } + + foreach (var stateId in TProjection.EnumerateRelevantStateIds(source)) + { + var previousProjection = await projectionRepository.StateRepository + .Get(stateId, cancellationToken); + + var nextProjection = previousProjection == null + ? TProjection.Construct(stateId) + : previousProjection; + + nextProjection.Mutate(source); + + var nextProjectionPointer = nextProjection.GetPointer(); + + if (nextProjection.ShouldRecordAsLatest(previousProjection)) + { + await projectionRepository.StateRepository.Put(nextProjectionPointer.Id, nextProjection, + cancellationToken); + } + + if (nextProjection.ShouldRecord()) + { + await projectionRepository.StateRepository.Put(nextProjectionPointer, nextProjection, + cancellationToken); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + } + + internal static ProjectionStateSourceProcessor Create(IServiceProvider serviceProvider, + string stateSessionOptionsName) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, + stateSessionOptionsName); + } +} diff --git a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueExtensions.cs similarity index 87% rename from src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs rename to src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueExtensions.cs index d7ce7a2e..16daeee0 100644 --- a/src/EntityDb.Common/Extensions/SourceProcessorQueueExtensions.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueExtensions.cs @@ -1,8 +1,6 @@ using EntityDb.Abstractions.Sources; -using EntityDb.Common.Sources.Processors; -using EntityDb.Common.Sources.Processors.Queues; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources.Processors.Queues; /// /// Extensions for . diff --git a/src/EntityDb.Common/Sources/PublishSourceRepository.cs b/src/EntityDb.Common/Sources/PublishSourceRepository.cs index 19bf12f8..a81c6c89 100644 --- a/src/EntityDb.Common/Sources/PublishSourceRepository.cs +++ b/src/EntityDb.Common/Sources/PublishSourceRepository.cs @@ -32,7 +32,7 @@ public override async Task Commit(Source source, CancellationToken cancell return true; } - + public static ISourceRepository Create(IServiceProvider serviceProvider, ISourceRepository sourceRepository) { diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs index 068ef193..4512aa35 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs @@ -1,15 +1,16 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Queries.SortBuilders; namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedLeaseQuery - (ILeaseQuery LeaseQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(LeaseQuery, - ModifiedQueryOptions), ILeaseQuery +internal sealed record ModifiedLeaseQuery : ModifiedQueryBase, ILeaseQuery { - public TFilter GetFilter(ILeaseFilterBuilder builder) + public required ILeaseQuery LeaseQuery { get; init; } + protected override IQuery Query => LeaseQuery; + + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { if (ModifiedQueryOptions.InvertFilter) { diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs new file mode 100644 index 00000000..c7889888 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Common.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.Modified; + +internal sealed record ModifiedMessageDataQuery : ModifiedQueryBase, IMessageDataQuery +{ + public required IMessageDataQuery MessageDataQuery { get; init; } + protected override IQuery Query => MessageDataQuery; + + public TFilter GetFilter(IMessageDataFilterBuilder builder) + { + if (ModifiedQueryOptions.InvertFilter) + { + return builder.Not + ( + MessageDataQuery.GetFilter(builder) + ); + } + + return MessageDataQuery.GetFilter(builder); + } + + public TSort? GetSort(IMessageDataSortBuilder builder) + { + return MessageDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + ? builder.Reverse() + : builder); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs deleted file mode 100644 index 8ce1a7f5..00000000 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageGroupQuery.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Extensions; - -namespace EntityDb.Common.Sources.Queries.Modified; - -internal sealed record ModifiedMessageGroupQuery - (IMessageGroupQuery MessageGroupQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase( - MessageGroupQuery, - ModifiedQueryOptions), IMessageGroupQuery -{ - public TFilter GetFilter(IMessageGroupFilterBuilder builder) - { - if (ModifiedQueryOptions.InvertFilter) - { - return builder.Not - ( - MessageGroupQuery.GetFilter(builder) - ); - } - - return MessageGroupQuery.GetFilter(builder); - } - - public TSort? GetSort(IMessageGroupSortBuilder builder) - { - return MessageGroupQuery.GetSort(ModifiedQueryOptions.ReverseSort - ? builder.Reverse() - : builder); - } -} diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs deleted file mode 100644 index 55323563..00000000 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageQuery.cs +++ /dev/null @@ -1,31 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Extensions; - -namespace EntityDb.Common.Sources.Queries.Modified; - -internal sealed record ModifiedMessageQuery - (IMessageQuery MessageQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(MessageQuery, - ModifiedQueryOptions), IMessageQuery -{ - public TFilter GetFilter(IMessageFilterBuilder builder) - { - if (ModifiedQueryOptions.InvertFilter) - { - return builder.Not - ( - MessageQuery.GetFilter(builder) - ); - } - - return MessageQuery.GetFilter(builder); - } - - public TSort? GetSort(IMessageSortBuilder builder) - { - return MessageQuery.GetSort(ModifiedQueryOptions.ReverseSort - ? builder.Reverse() - : builder); - } -} diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs index 6ffc46f7..60bbaaf4 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs @@ -2,8 +2,11 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal abstract record ModifiedQueryBase(IQuery Query, ModifiedQueryOptions ModifiedQueryOptions) +internal abstract record ModifiedQueryBase { + protected abstract IQuery Query { get; } + public required ModifiedQueryOptions ModifiedQueryOptions { get; init; } + public int? Skip => ModifiedQueryOptions.ReplaceSkip ?? Query.Skip; public int? Take => ModifiedQueryOptions.ReplaceTake ?? Query.Take; diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs new file mode 100644 index 00000000..3c2c2522 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Common.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.Modified; + +internal sealed record ModifiedSourceDataQuery : ModifiedQueryBase, ISourceDataQuery +{ + public required ISourceDataQuery SourceDataQuery { get; init; } + protected override IQuery Query => SourceDataQuery; + + public TFilter GetFilter(ISourceDataFilterBuilder builder) + { + if (ModifiedQueryOptions.InvertFilter) + { + return builder.Not + ( + SourceDataQuery.GetFilter(builder) + ); + } + + return SourceDataQuery.GetFilter(builder); + } + + public TSort? GetSort(ISourceDataSortBuilder builder) + { + return SourceDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + ? builder.Reverse() + : builder); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs index 6d5e5434..49b003e6 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs @@ -1,15 +1,16 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Extensions; +using EntityDb.Common.Sources.Queries.SortBuilders; namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedTagQuery - (ITagQuery TagQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(TagQuery, - ModifiedQueryOptions), ITagQuery +internal sealed record ModifiedTagQuery : ModifiedQueryBase, ITagQuery { - public TFilter GetFilter(ITagFilterBuilder builder) + public required ITagQuery TagQuery { get; init; } + protected override IQuery Query => TagQuery; + + public TFilter GetFilter(ITagDataFilterBuilder builder) { if (ModifiedQueryOptions.InvertFilter) { diff --git a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs index 64702db0..3934cd7a 100644 --- a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs @@ -9,28 +9,36 @@ namespace EntityDb.Common.Sources.Queries; public static class QueryExtensions { /// - /// Returns a new, modified . The way in which + /// Returns a new, modified . The way in which /// it is modified depends on the parameters of this extension method. /// - /// The message group query. + /// The source data query. /// The options for modifying the query. - /// A new, modified . - public static IMessageGroupQuery Modify(this IMessageGroupQuery messageGroupQuery, + /// A new, modified . + public static ISourceDataQuery Modify(this ISourceDataQuery sourceDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedMessageGroupQuery(messageGroupQuery, modifiedQueryOptions); + return new ModifiedSourceDataQuery + { + ModifiedQueryOptions = modifiedQueryOptions, SourceDataQuery = sourceDataQuery, + }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// Returns a new, modified . The way in which it is modified depends on the parameters + /// of /// this extension method. /// - /// The message query. + /// The message data query. /// The options for modifying the query. - /// A new, modified . - public static IMessageQuery Modify(this IMessageQuery messageQuery, ModifiedQueryOptions modifiedQueryOptions) + /// A new, modified . + public static IMessageDataQuery Modify(this IMessageDataQuery messageDataQuery, + ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedMessageQuery(messageQuery, modifiedQueryOptions); + return new ModifiedMessageDataQuery + { + ModifiedQueryOptions = modifiedQueryOptions, MessageDataQuery = messageDataQuery, + }; } /// @@ -42,7 +50,7 @@ public static IMessageQuery Modify(this IMessageQuery messageQuery, ModifiedQuer /// A new, modified . public static ILeaseQuery Modify(this ILeaseQuery leaseQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedLeaseQuery(leaseQuery, modifiedQueryOptions); + return new ModifiedLeaseQuery { ModifiedQueryOptions = modifiedQueryOptions, LeaseQuery = leaseQuery }; } /// @@ -54,6 +62,6 @@ public static ILeaseQuery Modify(this ILeaseQuery leaseQuery, ModifiedQueryOptio /// A new, modified . public static ITagQuery Modify(this ITagQuery tagQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedTagQuery(tagQuery, modifiedQueryOptions); + return new ModifiedTagQuery { ModifiedQueryOptions = modifiedQueryOptions, TagQuery = tagQuery }; } } diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs index 04adedd8..68bc45de 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs @@ -2,18 +2,19 @@ namespace EntityDb.Common.Sources.Queries.SortBuilders; -internal sealed record ReverseLeaseSortBuilder - (ILeaseSortBuilder LeaseSortBuilder) : ReverseSortBuilderBase(LeaseSortBuilder), - ILeaseSortBuilder +internal sealed record ReverseLeaseSortBuilder : ReverseSortBuilderBase, ILeaseSortBuilder { - public TSort EntityId(bool ascending) + public required ILeaseSortBuilder LeaseSortBuilder { get; init; } + protected override ISortBuilder SortBuilder => LeaseSortBuilder; + + public TSort StateId(bool ascending) { - return LeaseSortBuilder.EntityId(!ascending); + return LeaseSortBuilder.StateId(!ascending); } - public TSort EntityVersion(bool ascending) + public TSort StateVersion(bool ascending) { - return LeaseSortBuilder.EntityVersion(!ascending); + return LeaseSortBuilder.StateVersion(!ascending); } public TSort LeaseScope(bool ascending) diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs new file mode 100644 index 00000000..063547ba --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal sealed record ReverseMessageDataSortBuilder : ReverseSortBuilderBase, + IMessageDataSortBuilder +{ + public required IMessageDataSortBuilder MessageDataSortBuilder { get; init; } + protected override ISortBuilder SortBuilder => MessageDataSortBuilder; + + public TSort StateId(bool ascending) + { + return MessageDataSortBuilder.StateId(!ascending); + } + + public TSort StateVersion(bool ascending) + { + return MessageDataSortBuilder.StateVersion(!ascending); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs deleted file mode 100644 index 1bdf935c..00000000 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageGroupSortBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.SortBuilders; - -namespace EntityDb.Common.Sources.Queries.SortBuilders; - -internal sealed record ReverseMessageGroupSortBuilder - (IMessageGroupSortBuilder MessageGroupSortBuilder) : ReverseSortBuilderBase( - MessageGroupSortBuilder), - IMessageGroupSortBuilder -{ - public TSort EntityIds(bool ascending) - { - return MessageGroupSortBuilder.EntityIds(!ascending); - } -} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs deleted file mode 100644 index 6fa9b277..00000000 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageSortBuilder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.SortBuilders; - -namespace EntityDb.Common.Sources.Queries.SortBuilders; - -internal sealed record ReverseMessageSortBuilder - (IMessageSortBuilder MessageSortBuilder) : ReverseSortBuilderBase(MessageSortBuilder), - IMessageSortBuilder -{ - public TSort EntityId(bool ascending) - { - return MessageSortBuilder.EntityId(!ascending); - } - - public TSort EntityVersion(bool ascending) - { - return MessageSortBuilder.EntityVersion(!ascending); - } -} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs index e7fe29d9..8fb844b0 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs @@ -2,8 +2,10 @@ namespace EntityDb.Common.Sources.Queries.SortBuilders; -internal abstract record ReverseSortBuilderBase(ISortBuilder SortBuilder) +internal abstract record ReverseSortBuilderBase { + protected abstract ISortBuilder SortBuilder { get; } + public TSort SourceTimeStamp(bool ascending) { return SortBuilder.SourceTimeStamp(!ascending); diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs new file mode 100644 index 00000000..130d98a0 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal sealed record ReverseSourceDataSortBuilder : ReverseSortBuilderBase, + ISourceDataSortBuilder +{ + public required ISourceDataSortBuilder SourceDataSortBuilder { get; init; } + protected override ISortBuilder SortBuilder => SourceDataSortBuilder; + + public TSort StateIds(bool ascending) + { + return SourceDataSortBuilder.StateIds(!ascending); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs index e240ad9f..a13a1f5e 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs @@ -2,17 +2,19 @@ namespace EntityDb.Common.Sources.Queries.SortBuilders; -internal sealed record ReverseTagSortBuilder - (ITagSortBuilder TagSortBuilder) : ReverseSortBuilderBase(TagSortBuilder), ITagSortBuilder +internal sealed record ReverseTagSortBuilder : ReverseSortBuilderBase, ITagSortBuilder { - public TSort EntityId(bool ascending) + public required ITagSortBuilder TagSortBuilder { get; init; } + protected override ISortBuilder SortBuilder => TagSortBuilder; + + public TSort StateId(bool ascending) { - return TagSortBuilder.EntityId(!ascending); + return TagSortBuilder.StateId(!ascending); } - public TSort EntityVersion(bool ascending) + public TSort StateVersion(bool ascending) { - return TagSortBuilder.EntityVersion(!ascending); + return TagSortBuilder.StateVersion(!ascending); } public TSort TagLabel(bool ascending) diff --git a/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs similarity index 61% rename from src/EntityDb.Common/Extensions/SortBuilderExtensions.cs rename to src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs index 6aaef704..09022e6f 100644 --- a/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Sources.Queries.SortBuilders; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources.Queries.SortBuilders; /// /// Extensions for sort builders. @@ -9,33 +8,33 @@ namespace EntityDb.Common.Extensions; public static class SortBuilderExtensions { /// - /// Returns a that orders objects in the reverse order of - /// another . + /// Returns a that orders objects in the reverse order of + /// another . /// /// The type of sort used by the repository. /// The message group sort builder. /// - /// A that orders objects in the reverse order of + /// A that orders objects in the reverse order of /// . /// - public static IMessageGroupSortBuilder Reverse(this IMessageGroupSortBuilder builder) + public static ISourceDataSortBuilder Reverse(this ISourceDataSortBuilder builder) { - return new ReverseMessageGroupSortBuilder(builder); + return new ReverseSourceDataSortBuilder { SourceDataSortBuilder = builder }; } /// - /// Returns a that orders objects in the reverse order of another - /// . + /// Returns a that orders objects in the reverse order of another + /// . /// /// The type of sort used by the repository. /// The message sort builder. /// - /// A that orders message in the reverse order of + /// A that orders message in the reverse order of /// . /// - public static IMessageSortBuilder Reverse(this IMessageSortBuilder builder) + public static IMessageDataSortBuilder Reverse(this IMessageDataSortBuilder builder) { - return new ReverseMessageSortBuilder(builder); + return new ReverseMessageDataSortBuilder { MessageDataSortBuilder = builder }; } /// @@ -50,7 +49,7 @@ public static IMessageSortBuilder Reverse(this IMessageSortBuilder /// public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder builder) { - return new ReverseLeaseSortBuilder(builder); + return new ReverseLeaseSortBuilder { LeaseSortBuilder = builder }; } /// @@ -65,6 +64,6 @@ public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder public static ITagSortBuilder Reverse(this ITagSortBuilder builder) { - return new ReverseTagSortBuilder(builder); + return new ReverseTagSortBuilder { TagSortBuilder = builder }; } } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs index 7cd5aba2..2678317e 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs @@ -1,14 +1,14 @@ -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record DeleteLeasesQuery(IReadOnlyCollection Leases, object? Options = null) : ILeaseQuery { - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.Or ( diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs index cf75f5b1..3749ca9c 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs @@ -1,19 +1,19 @@ -using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.States.Attributes; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record DeleteTagsQuery - (Id EntityId, IReadOnlyCollection Tags, object? Options = null) : ITagQuery + (Id StateId, IReadOnlyCollection Tags, object? Options = null) : ITagQuery { - public TFilter GetFilter(ITagFilterBuilder builder) + public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.And ( - builder.EntityIdIn(EntityId), + builder.StateIdIn(StateId), builder.Or ( Tags diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs index f801d418..1af0442b 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs @@ -6,29 +6,29 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetDeltasQuery(Pointer EntityPointer, Version SnapshotVersion, - object? Options = null) : IMessageQuery +internal sealed record GetDeltasQuery(Pointer StatePointer, Version PersistedStateVersion, + object? Options = null) : IMessageDataQuery { - public TFilter GetFilter(IMessageFilterBuilder builder) + public TFilter GetFilter(IMessageDataFilterBuilder builder) { var filters = new List { - builder.EntityIdIn(EntityPointer.Id), builder.EntityVersionGte(EntityPointer.Version.Next()), + builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(StatePointer.Version.Next()), }; - if (SnapshotVersion != Version.Zero) + if (PersistedStateVersion != Version.Zero) { - filters.Add(builder.EntityVersionLte(SnapshotVersion)); + filters.Add(builder.StateVersionLte(PersistedStateVersion)); } return builder.And(filters.ToArray()); } - public TSort GetSort(IMessageSortBuilder builder) + public TSort GetSort(IMessageDataSortBuilder builder) { return builder.Combine ( - builder.EntityVersion(true) + builder.StateVersion(true) ); } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs deleted file mode 100644 index fb4a82f3..00000000 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetLastEntityVersionQuery.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Sources.Queries.Standard; - -internal sealed record GetLastEntityVersionQuery(Id EntityId, object? Options = null) : IMessageQuery -{ - public TFilter GetFilter(IMessageFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(IMessageSortBuilder builder) - { - return builder.EntityVersion(false); - } - - public int? Skip => null; - - public int? Take => 1; -} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs new file mode 100644 index 00000000..ac0fd628 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs @@ -0,0 +1,23 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record GetLastStateVersionQuery(Id StateId, object? Options = null) : IMessageDataQuery +{ + public TFilter GetFilter(IMessageDataFilterBuilder builder) + { + return builder.StateIdIn(StateId); + } + + public TSort GetSort(IMessageDataSortBuilder builder) + { + return builder.StateVersion(false); + } + + public int? Skip => null; + + public int? Take => 1; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs index afc6baa4..ca36f8ba 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs @@ -5,31 +5,31 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetSourceQuery(Id SourceId) : IMessageGroupQuery, IMessageQuery +internal sealed record GetSourceQuery(Id SourceId) : ISourceDataQuery, IMessageDataQuery { - public TFilter GetFilter(IMessageGroupFilterBuilder builder) + public TFilter GetFilter(IMessageDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } - public TSort? GetSort(IMessageGroupSortBuilder builder) + public TSort? GetSort(IMessageDataSortBuilder builder) { return default; } - public int? Skip => default; - - public int? Take => default; - - public object? Options => default; - - public TFilter GetFilter(IMessageFilterBuilder builder) + public TFilter GetFilter(ISourceDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } - public TSort? GetSort(IMessageSortBuilder builder) + public TSort? GetSort(ISourceDataSortBuilder builder) { return default; } + + public int? Skip => default; + + public int? Take => default; + + public object? Options => default; } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs index 27778572..40aca360 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs @@ -1,13 +1,13 @@ -using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record MatchingLeaseQuery(ILease Lease) : ILeaseQuery { - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.And ( diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs index e5ee045f..3355a99f 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs @@ -1,13 +1,13 @@ -using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record MatchingLeasesQuery(params ILease[] Leases) : ILeaseQuery { - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.Or ( diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs index 06559bef..eaf10a55 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.Sources; -using EntityDb.Common.Extensions; using EntityDb.Common.Sources.Processors.Queues; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -46,7 +45,7 @@ private async Task Process(ISourceReprocessorQueueItem item, CancellationToken c _logger.LogDebug("Started reprocessing sources"); await using var sourceRepository = - await _sourceRepositoryFactory.CreateRepository(item.SourceSessionOptionsName, + await _sourceRepositoryFactory.Create(item.SourceSessionOptionsName, cancellationToken); var sourceIds = await sourceRepository diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs index d2394889..5a0aa343 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs @@ -11,7 +11,7 @@ public interface ISourceReprocessorQueueItem { /// /// The name of the source session options passed to - /// + /// /// string SourceSessionOptionsName { get; } diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs index 6d89217c..97ec9dd4 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.Sources; -using EntityDb.Common.Extensions; using EntityDb.Common.Sources.Processors.Queues; using Microsoft.Extensions.Logging; @@ -31,7 +30,7 @@ private async Task Process(ISourceReprocessorQueueItem item, CancellationToken c _logger.LogDebug("Started reprocessing sources"); await using var sourceRepository = - await _sourceRepositoryFactory.CreateRepository(item.SourceSessionOptionsName, + await _sourceRepositoryFactory.Create(item.SourceSessionOptionsName, cancellationToken); var sourceIds = await sourceRepository diff --git a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs similarity index 88% rename from src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs rename to src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs index 0c7ccdb2..2ce7477d 100644 --- a/src/EntityDb.Common/Extensions/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs @@ -2,9 +2,8 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Sources.Queries.Standard; -using System.Collections.Immutable; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources; /// /// Extensions for . @@ -23,9 +22,9 @@ public static IAsyncEnumerable EnumerateSourceIds(this ISourceRepository sou { return query switch { - IMessageGroupQuery messageGroupQuery => sourceRepository.EnumerateSourceIds(messageGroupQuery, + ISourceDataQuery sourceDataQuery => sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken), + IMessageDataQuery messageDataQuery => sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken), - IMessageQuery messageQuery => sourceRepository.EnumerateSourceIds(messageQuery, cancellationToken), ILeaseQuery leaseQuery => sourceRepository.EnumerateSourceIds(leaseQuery, cancellationToken), ITagQuery tagQuery => sourceRepository.EnumerateSourceIds(tagQuery, cancellationToken), _ => AsyncEnumerable.Empty(), @@ -75,7 +74,7 @@ public static async Task GetSource .Select(annotatedDelta => new Message { Id = annotatedDelta.MessageId, - EntityPointer = annotatedDelta.EntityPointer, + StatePointer = annotatedDelta.StatePointer, Delta = annotatedDelta.Data, }) .ToArrayAsync(cancellationToken); @@ -85,7 +84,7 @@ public static async Task GetSource Id = annotatedAgentSignature.SourceId, TimeStamp = annotatedAgentSignature.SourceTimeStamp, AgentSignature = annotatedAgentSignature.Data, - Messages = messages.ToImmutableArray(), + Messages = messages.ToArray(), }; } } diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs index f62f611f..f45f1d5e 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States.Attributes; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; @@ -16,16 +16,16 @@ protected SourceRepositoryWrapper(ISourceRepository sourceRepository) _sourceRepository = sourceRepository; } - public IAsyncEnumerable EnumerateSourceIds(IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageGroupQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(IMessageQuery messageQuery, + public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken)); } public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, @@ -40,40 +40,40 @@ public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateEntityPointers(IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(messageGroupQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateEntityPointers(IMessageQuery messageQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(messageQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateEntityPointers(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(leaseQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateEntityPointers(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagQuery tagQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateEntityPointers(tagQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateAgentSignatures(IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(messageGroupQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateDeltas(IMessageQuery messageQuery, + public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageDataQuery, cancellationToken)); } public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, @@ -88,18 +88,18 @@ public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, return WrapQuery(() => _sourceRepository.EnumerateTags(tagQuery, cancellationToken)); } - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => - _sourceRepository.EnumerateAnnotatedAgentSignatures(messageGroupQuery, cancellationToken)); + _sourceRepository.EnumerateAnnotatedAgentSignatures(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageQuery messageQuery, + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageDataQuery, cancellationToken)); } public virtual Task Commit(Source source, diff --git a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs similarity index 66% rename from src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs rename to src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs index b612cd6e..84a24ca7 100644 --- a/src/EntityDb.Common/Sources/Subscribers/EntitySnapshotSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs @@ -1,17 +1,16 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; -using EntityDb.Common.Extensions; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; namespace EntityDb.Common.Sources.Subscribers; -internal class EntitySnapshotSourceSubscriber : ISourceSubscriber +internal class EntityStateSourceSubscriber : ISourceSubscriber where TEntity : IEntity { private readonly ISourceProcessorQueue _sourceProcessorQueue; - public EntitySnapshotSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) + public EntityStateSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) { _sourceProcessorQueue = sourceProcessorQueue; } @@ -23,6 +22,6 @@ public void Notify(Source source) return; } - _sourceProcessorQueue.Enqueue>(source); + _sourceProcessorQueue.Enqueue>(source); } } diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs similarity index 58% rename from src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs rename to src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs index 63c72067..47ddb307 100644 --- a/src/EntityDb.Common/Sources/Subscribers/ProjectionSnapshotSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs @@ -1,28 +1,27 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; -using EntityDb.Common.Extensions; using EntityDb.Common.Sources.Processors; using EntityDb.Common.Sources.Processors.Queues; namespace EntityDb.Common.Sources.Subscribers; -internal class ProjectionSnapshotSourceSubscriber : ISourceSubscriber +internal class ProjectionStateSourceSubscriber : ISourceSubscriber where TProjection : IProjection { private readonly ISourceProcessorQueue _sourceProcessorQueue; - public ProjectionSnapshotSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) + public ProjectionStateSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) { _sourceProcessorQueue = sourceProcessorQueue; } public void Notify(Source source) { - if (!TProjection.EnumerateEntityIds(source).Any()) + if (!TProjection.EnumerateRelevantStateIds(source).Any()) { return; } - _sourceProcessorQueue.Enqueue>(source); + _sourceProcessorQueue.Enqueue>(source); } } diff --git a/src/EntityDb.Common/Sources/Attributes/Lease.cs b/src/EntityDb.Common/States/Attributes/Lease.cs similarity index 52% rename from src/EntityDb.Common/Sources/Attributes/Lease.cs rename to src/EntityDb.Common/States/Attributes/Lease.cs index 9b55cda4..7375cdc5 100644 --- a/src/EntityDb.Common/Sources/Attributes/Lease.cs +++ b/src/EntityDb.Common/States/Attributes/Lease.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; -namespace EntityDb.Common.Sources.Attributes; +namespace EntityDb.Common.States.Attributes; /// public sealed record Lease(string Scope, string Label, string Value) : ILease; diff --git a/src/EntityDb.Common/States/Attributes/Tag.cs b/src/EntityDb.Common/States/Attributes/Tag.cs new file mode 100644 index 00000000..86cacd69 --- /dev/null +++ b/src/EntityDb.Common/States/Attributes/Tag.cs @@ -0,0 +1,6 @@ +using EntityDb.Abstractions.States.Attributes; + +namespace EntityDb.Common.States.Attributes; + +/// +public sealed record Tag(string Label, string Value) : ITag; diff --git a/src/EntityDb.Common/States/StateRepositoryFactoryExtensions.cs b/src/EntityDb.Common/States/StateRepositoryFactoryExtensions.cs new file mode 100644 index 00000000..81d25f8c --- /dev/null +++ b/src/EntityDb.Common/States/StateRepositoryFactoryExtensions.cs @@ -0,0 +1,19 @@ +using EntityDb.Abstractions.States; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.Common.States; + +internal static class StateRepositoryFactoryExtensions +{ + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static IStateRepositoryFactory UseTestMode + ( + this IStateRepositoryFactory stateRepositoryFactory, + bool testMode + ) + { + return testMode + ? new TestModeStateRepositoryFactory(stateRepositoryFactory) + : stateRepositoryFactory; + } +} diff --git a/src/EntityDb.Common/States/StateRepositoryWrapper.cs b/src/EntityDb.Common/States/StateRepositoryWrapper.cs new file mode 100644 index 00000000..e33a37fb --- /dev/null +++ b/src/EntityDb.Common/States/StateRepositoryWrapper.cs @@ -0,0 +1,45 @@ +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.States; + +internal abstract class StateRepositoryWrapper : DisposableResourceBaseClass, + IStateRepository +{ + private readonly IStateRepository _stateRepository; + + protected StateRepositoryWrapper + ( + IStateRepository stateRepository + ) + { + _stateRepository = stateRepository; + } + + public virtual Task Put(Pointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + return WrapCommand(() => _stateRepository.Put(statePointer, state, cancellationToken)); + } + + public virtual Task Get(Pointer statePointer, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _stateRepository.Get(statePointer, cancellationToken)); + } + + public virtual Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + { + return WrapCommand(() => _stateRepository.Delete(statePointers, cancellationToken)); + } + + public override async ValueTask DisposeAsync() + { + await _stateRepository.DisposeAsync(); + } + + protected abstract Task WrapQuery(Func> task); + + protected abstract Task WrapCommand(Func> task); +} diff --git a/src/EntityDb.Common/States/TestModeStateManager.cs b/src/EntityDb.Common/States/TestModeStateManager.cs new file mode 100644 index 00000000..74544ac7 --- /dev/null +++ b/src/EntityDb.Common/States/TestModeStateManager.cs @@ -0,0 +1,55 @@ +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.States; + +internal class TestModeStateManager : DisposableResourceBaseClass +{ + private readonly Dictionary, List> _dictionary = new(); + + private List GetStoredStatePointers(IStateRepository stateRepository) + { + if (_dictionary.TryGetValue(stateRepository, out var storedStatePointers)) + { + return storedStatePointers; + } + + storedStatePointers = new List(); + + _dictionary.Add(stateRepository, storedStatePointers); + + return storedStatePointers; + } + + public void AddStatePointer(IStateRepository stateRepository, Pointer statePointer) + { + var storedStatePointers = GetStoredStatePointers(stateRepository); + + storedStatePointers.Add(statePointer); + } + + public void RemoveStatePointers(IStateRepository stateRepository, + IEnumerable statePointers) + { + var storedStatePointers = GetStoredStatePointers(stateRepository); + + storedStatePointers.RemoveAll(statePointers.Contains); + + if (storedStatePointers.Count == 0) + { + _dictionary.Remove(stateRepository); + } + } + + /// + /// This should only be called by the state repository factory. + /// + public override async ValueTask DisposeAsync() + { + foreach (var (stateRepository, storedStatePointers) in _dictionary.ToArray()) + { + await stateRepository.Delete(storedStatePointers.ToArray()); + } + } +} diff --git a/src/EntityDb.Common/States/TestModeStateRepository.cs b/src/EntityDb.Common/States/TestModeStateRepository.cs new file mode 100644 index 00000000..a80cf50b --- /dev/null +++ b/src/EntityDb.Common/States/TestModeStateRepository.cs @@ -0,0 +1,47 @@ +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.States; + +internal sealed class TestModeStateRepository : DisposableResourceBaseClass, + IStateRepository +{ + private readonly IStateRepository _stateRepository; + private readonly TestModeStateManager _testModeStateManager; + + public TestModeStateRepository + ( + IStateRepository stateRepository, + TestModeStateManager testModeStateManager + ) + { + _stateRepository = stateRepository; + _testModeStateManager = testModeStateManager; + } + + public Task Put(Pointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + _testModeStateManager.AddStatePointer(this, statePointer); + + return _stateRepository.Put(statePointer, state, cancellationToken); + } + + public Task Get(Pointer statePointer, CancellationToken cancellationToken = default) + { + return _stateRepository.Get(statePointer, cancellationToken); + } + + public Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + { + _testModeStateManager.RemoveStatePointers(this, statePointers); + + return _stateRepository.Delete(statePointers, cancellationToken); + } + + public override async ValueTask DisposeAsync() + { + await _stateRepository.DisposeAsync(); + } +} diff --git a/src/EntityDb.Common/States/TestModeStateRepositoryFactory.cs b/src/EntityDb.Common/States/TestModeStateRepositoryFactory.cs new file mode 100644 index 00000000..ced25966 --- /dev/null +++ b/src/EntityDb.Common/States/TestModeStateRepositoryFactory.cs @@ -0,0 +1,34 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.States; + +internal sealed class TestModeStateRepositoryFactory : DisposableResourceBaseClass, + IStateRepositoryFactory +{ + private readonly IStateRepositoryFactory _stateRepositoryFactory; + private readonly TestModeStateManager _testModeStateManager = new(); + + public TestModeStateRepositoryFactory + ( + IStateRepositoryFactory stateRepositoryFactory + ) + { + _stateRepositoryFactory = stateRepositoryFactory; + } + + public async Task> Create(string stateSessionOptionsName, + CancellationToken cancellationToken = default) + { + var stateRepository = + await _stateRepositoryFactory.Create(stateSessionOptionsName, cancellationToken); + + return new TestModeStateRepository(stateRepository, _testModeStateManager); + } + + public override async ValueTask DisposeAsync() + { + await _testModeStateManager.DisposeAsync(); + await _stateRepositoryFactory.DisposeAsync(); + } +} diff --git a/src/EntityDb.Common/Snapshots/TryCatchSnapshotRepository.cs b/src/EntityDb.Common/States/TryCatchStateRepository.cs similarity index 53% rename from src/EntityDb.Common/Snapshots/TryCatchSnapshotRepository.cs rename to src/EntityDb.Common/States/TryCatchStateRepository.cs index fe2ea5c0..26d25699 100644 --- a/src/EntityDb.Common/Snapshots/TryCatchSnapshotRepository.cs +++ b/src/EntityDb.Common/States/TryCatchStateRepository.cs @@ -1,24 +1,24 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace EntityDb.Common.Snapshots; +namespace EntityDb.Common.States; -internal sealed class TryCatchSnapshotRepository : SnapshotRepositoryWrapper +internal sealed class TryCatchStateRepository : StateRepositoryWrapper { - private readonly ILogger> _logger; + private readonly ILogger> _logger; - public TryCatchSnapshotRepository + public TryCatchStateRepository ( - ILogger> logger, - ISnapshotRepository snapshotRepository + ILogger> logger, + IStateRepository stateRepository ) - : base(snapshotRepository) + : base(stateRepository) { _logger = logger; } - protected override async Task WrapQuery(Func> task) + protected override async Task WrapQuery(Func> task) { using (_logger.BeginScope("TryCatchId: {TryCatchId}", Guid.NewGuid())) { @@ -52,10 +52,10 @@ protected override async Task WrapCommand(Func> task) } } - public static ISnapshotRepository Create(IServiceProvider serviceProvider, - ISnapshotRepository snapshotRepository) + public static IStateRepository Create(IServiceProvider serviceProvider, + IStateRepository stateRepository) { - return ActivatorUtilities.CreateInstance>(serviceProvider, - snapshotRepository); + return ActivatorUtilities.CreateInstance>(serviceProvider, + stateRepository); } } diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs new file mode 100644 index 00000000..df083e0c --- /dev/null +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -0,0 +1,128 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Streams; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.Common.States.Attributes; + +namespace EntityDb.Common.Streams; + +internal sealed class MultipleStreamRepository : DisposableResourceBaseClass, IMultipleStreamRepository +{ + private const string RootScope = "Stream"; + private const string StreamKeyLabel = "StreamKey"; + private const string MessageKeyLabel = "MessageKey"; + private readonly IAgent _agent; + private readonly Dictionary _knownStreams = new(); + private readonly List _messages = new(); + + public MultipleStreamRepository(IAgent agent, ISourceRepository sourceRepository) + { + _agent = agent; + + SourceRepository = sourceRepository; + } + + public ISourceRepository SourceRepository { get; } + + public async Task LoadOrCreate(Key streamKey, CancellationToken cancellationToken = default) + { + if (_knownStreams.ContainsKey(streamKey)) + { + throw new ExistingStreamException(); + } + + var streamKeyLease = GetStreamKeyLease(streamKey); + + var streamPointer = await GetStreamPointer(streamKeyLease, cancellationToken); + + var stream = streamPointer == default + ? new Stream { Key = streamKey, Id = Id.NewId(), New = true } + : new Stream { Key = streamKey, Id = streamPointer.Id, New = false }; + + _knownStreams.Add(streamKey, stream); + } + + public async Task Stage(Key streamKey, Key messageKey, object delta, + CancellationToken cancellationToken = default) + { + if (!_knownStreams.TryGetValue(streamKey, out var stream)) + { + throw new UnknownStreamException(); + } + + var messageKeyLease = GetMessageKeyLease(streamKey, messageKey); + + var statePointer = await GetStreamPointer(messageKeyLease, cancellationToken); + + if (statePointer != default) + { + return false; + } + + var addLeases = new List { messageKeyLease }; + + if (stream.New) + { + addLeases.Add(GetStreamKeyLease(stream.Key)); + + _knownStreams[streamKey] = stream with { New = false }; + } + + _messages.Add(new Message + { + Id = Id.NewId(), StatePointer = stream.Id, Delta = delta, AddLeases = addLeases.ToArray(), + }); + + return true; + } + + public async Task Commit(CancellationToken cancellationToken = default) + { + var source = new Source + { + Id = Id.NewId(), + TimeStamp = _agent.TimeStamp, + AgentSignature = _agent.Signature, + Messages = _messages.ToArray(), + }; + + var committed = await SourceRepository.Commit(source, cancellationToken); + + if (!committed) + { + return false; + } + + _messages.Clear(); + + return true; + } + + public override ValueTask DisposeAsync() + { + return SourceRepository.DisposeAsync(); + } + + public static ILease GetStreamKeyLease(Key streamKey) + { + return new Lease(RootScope, StreamKeyLabel, streamKey.Value); + } + + public static ILease GetMessageKeyLease(Key streamKey, Key messageKey) + { + return new Lease($"{RootScope}/{streamKey}", MessageKeyLabel, messageKey.Value); + } + + private async Task GetStreamPointer(ILease lease, CancellationToken cancellationToken) + { + var query = new MatchingLeaseQuery(lease); + + return await SourceRepository + .EnumerateStatePointers(query, cancellationToken) + .SingleOrDefaultAsync(cancellationToken); + } +} diff --git a/src/EntityDb.Common/Streams/SingleEntityRepository.cs b/src/EntityDb.Common/Streams/SingleEntityRepository.cs new file mode 100644 index 00000000..09491c95 --- /dev/null +++ b/src/EntityDb.Common/Streams/SingleEntityRepository.cs @@ -0,0 +1,36 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Streams; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.Streams; + +internal sealed class SingleStreamRepository : DisposableResourceBaseClass, ISingleStreamRepository +{ + private readonly IMultipleStreamRepository _multipleStreamRepository; + + public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository, Key streamKey) + { + _multipleStreamRepository = multipleStreamRepository; + + StreamKey = streamKey; + } + + public ISourceRepository SourceRepository => _multipleStreamRepository.SourceRepository; + public Key StreamKey { get; } + + public Task Stage(Key messageKey, object delta, CancellationToken cancellationToken = default) + { + return _multipleStreamRepository.Stage(StreamKey, messageKey, delta, cancellationToken); + } + + public Task Commit(CancellationToken cancellationToken = default) + { + return _multipleStreamRepository.Commit(cancellationToken); + } + + public override ValueTask DisposeAsync() + { + return _multipleStreamRepository.DisposeAsync(); + } +} diff --git a/src/EntityDb.Common/Streams/Stream.cs b/src/EntityDb.Common/Streams/Stream.cs new file mode 100644 index 00000000..71575732 --- /dev/null +++ b/src/EntityDb.Common/Streams/Stream.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Streams; + +internal sealed record Stream +{ + public required Key Key { get; init; } + public required Id Id { get; init; } + public required bool New { get; init; } +} diff --git a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs new file mode 100644 index 00000000..7d8bd031 --- /dev/null +++ b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs @@ -0,0 +1,57 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Streams; +using EntityDb.Abstractions.ValueObjects; +using Microsoft.Extensions.DependencyInjection; + +namespace EntityDb.Common.Streams; + +internal sealed class StreamRepositoryFactory : IStreamRepositoryFactory +{ + private readonly IAgentAccessor _agentAccessor; + private readonly IServiceProvider _serviceProvider; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + + public StreamRepositoryFactory + ( + IServiceProvider serviceProvider, + IAgentAccessor agentAccessor, + ISourceRepositoryFactory sourceRepositoryFactory + ) + { + _serviceProvider = serviceProvider; + _agentAccessor = agentAccessor; + _sourceRepositoryFactory = sourceRepositoryFactory; + } + + public async Task CreateSingle + ( + Key streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ) + { + var multipleStreamRepository = + await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, cancellationToken); + + await multipleStreamRepository.LoadOrCreate(streamKey, cancellationToken); + + return new SingleStreamRepository(multipleStreamRepository, streamKey); + } + + public async Task CreateMultiple + ( + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken + ) + { + var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); + + var sourceRepository = await _sourceRepositoryFactory + .Create(sourceSessionOptionsName, cancellationToken); + + return ActivatorUtilities.CreateInstance(_serviceProvider, agent, sourceRepository); + } +} diff --git a/src/EntityDb.Common/packages.lock.json b/src/EntityDb.Common/packages.lock.json index 23d4d5b2..7e44492f 100644 --- a/src/EntityDb.Common/packages.lock.json +++ b/src/EntityDb.Common/packages.lock.json @@ -22,6 +22,28 @@ "System.Linq.Async": "[6.0.1, )" } } + }, + "net8.0": { + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + } } } } \ No newline at end of file diff --git a/src/EntityDb.EntityFramework/Converters/IdConverter.cs b/src/EntityDb.EntityFramework/Converters/IdConverter.cs deleted file mode 100644 index 62362328..00000000 --- a/src/EntityDb.EntityFramework/Converters/IdConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using System.Linq.Expressions; - -namespace EntityDb.EntityFramework.Converters; - -internal class IdConverter : ValueConverter -{ - private static readonly Expression> IdToGuid = id => id.Value; - private static readonly Expression> GuidToId = guid => new Id(guid); - - public IdConverter() : base(IdToGuid, GuidToId) - { - } -} diff --git a/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs b/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs deleted file mode 100644 index 8235d7ac..00000000 --- a/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using System.Linq.Expressions; - -namespace EntityDb.EntityFramework.Converters; - -internal class TimeStampConverter : ValueConverter -{ - private static readonly Expression> TimeStampToDateTime = timeStamp => timeStamp.Value; - private static readonly Expression> DateTimeToTimeStamp = dateTime => new TimeStamp(dateTime); - - public TimeStampConverter() : base(TimeStampToDateTime, DateTimeToTimeStamp) - { - } -} diff --git a/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs b/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs deleted file mode 100644 index 5c4d2dcb..00000000 --- a/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using System.Linq.Expressions; - -namespace EntityDb.EntityFramework.Converters; - -internal class VersionNumberConverter : ValueConverter -{ - private static readonly Expression> VersionNumberToUlong = versionNumber => versionNumber.Value; - private static readonly Expression> UlongToVersionNumber = @ulong => new VersionNumber(@ulong); - - public VersionNumberConverter() : base(VersionNumberToUlong, UlongToVersionNumber) - { - } -} diff --git a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs deleted file mode 100644 index 3fb75b6f..00000000 --- a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextBase.cs +++ /dev/null @@ -1,27 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.EntityFramework.Converters; -using Microsoft.EntityFrameworkCore; - -namespace EntityDb.EntityFramework.DbContexts; - -/// -/// A DbContext that adds basic converters for types defined in -/// -public abstract class EntityDbContextBase : DbContext -{ - /// - protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) - { - configurationBuilder - .Properties() - .HaveConversion(); - - configurationBuilder - .Properties() - .HaveConversion(); - - configurationBuilder - .Properties() - .HaveConversion(); - } -} diff --git a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs deleted file mode 100644 index 3fecf263..00000000 --- a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs +++ /dev/null @@ -1,28 +0,0 @@ -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; - -namespace EntityDb.EntityFramework.DbContexts; - -internal class EntityDbContextFactory : IEntityDbContextFactory - where TDbContext : DbContext, IEntityDbContext -{ - private readonly IServiceProvider _serviceProvider; - private readonly IOptionsFactory _optionsFactory; - - public EntityDbContextFactory(IServiceProvider serviceProvider, IOptionsFactory optionsFactory) - { - _serviceProvider = serviceProvider; - _optionsFactory = optionsFactory; - } - - public TDbContext Create(string snapshotSessionOptionsName) - { - return TDbContext.Construct(_serviceProvider, _optionsFactory.Create(snapshotSessionOptionsName)); - } - - TDbContext IEntityDbContextFactory.Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) - { - return TDbContext.Construct(_serviceProvider, snapshotSessionOptions); - } -} diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs deleted file mode 100644 index af6660b0..00000000 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore; - -namespace EntityDb.EntityFramework.DbContexts; - -/// -/// A type of a that can be used for EntityDb purposes. -/// -/// The type of the -public interface IEntityDbContext - where TDbContext : DbContext, IEntityDbContext -{ - /// - /// Returns a new that will be configured using . - /// - /// A service provider for any injectable dependencies - /// The options for the database - /// A new that will be configured using . - static abstract TDbContext Construct(IServiceProvider serviceProvider, EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); -} diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs deleted file mode 100644 index 501fbbae..00000000 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore; - -namespace EntityDb.EntityFramework.DbContexts; - -/// -/// Represents a type used to create instances of . -/// -/// The type of the . -public interface IEntityDbContextFactory -{ - internal TDbContext Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); - - /// - /// Create a new instance of . - /// - /// The agent's use case for the . - /// A new instance of . - TDbContext Create(string snapshotSessionOptionsName); -} diff --git a/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj b/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj deleted file mode 100644 index b0cbe39f..00000000 --- a/src/EntityDb.EntityFramework/EntityDb.EntityFramework.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - - EntityDb EventSourcing DDD CQRS - An implementation of the EntityDb Snapshot Repository interface, specifically for Entity Framework. - - - - - - - - - - - diff --git a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index f161c7a7..00000000 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Extensions; -using EntityDb.EntityFramework.DbContexts; -using EntityDb.EntityFramework.Snapshots; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.EntityFramework.Extensions; - -/// -/// Extensions for service collections. -/// -[ExcludeFromCodeCoverage(Justification = "Don't need coverage for non-test mode.")] -public static class ServiceCollectionExtensions -{ - /// - /// Adds a production-ready implementation of to a service - /// collection. - /// - /// The type of the snapshot stored in the repository. - /// The type of the snapshot stored in the repository. - /// The service collection. - /// Modifies the behavior of the repository to accomodate tests. - public static void AddEntityFrameworkSnapshots(this IServiceCollection serviceCollection, bool testMode = false) - where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : DbContext, IEntityDbContext - { - serviceCollection.Add, EntityDbContextFactory> - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient - ); - - serviceCollection.Add> - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient - ); - - serviceCollection.Add> - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, - serviceProvider => serviceProvider - .GetRequiredService>() - .UseTestMode(serviceProvider, testMode) - ); - } -} diff --git a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs deleted file mode 100644 index 29a0326b..00000000 --- a/src/EntityDb.EntityFramework/Extensions/TestModeMongoDbTransactionRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.EntityFramework.Snapshots; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.EntityFramework.Extensions; - -internal static class EntityFrameworkSnapshotRepositoryFactoryExtensions -{ - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static IEntityFrameworkSnapshotRepositoryFactory UseTestMode( - this IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory, - IServiceProvider serviceProvider, - bool testMode) - { - return testMode - ? TestModeEntityFrameworkSnapshotRepositoryFactory.Create(serviceProvider, entityFrameworkSnapshotRepositoryFactory) - : entityFrameworkSnapshotRepositoryFactory; - } -} diff --git a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs b/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs deleted file mode 100644 index 8772b226..00000000 --- a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Linq.Expressions; - -namespace EntityDb.EntityFramework.Predicates; - -/// -/// Based on http://www.albahari.com/nutshell/predicatebuilder.aspx -/// -internal static class PredicateExpressionBuilder -{ - private static Expression> False() - { - return _ => false; - } - - private static Expression> Or - ( - Expression> left, - Expression> right - ) - { - return Expression.Lambda> - ( - Expression.OrElse - ( - left.Body, - Expression.Invoke(right, left.Parameters) - ), - left.Parameters - ); - } - - public static Expression> Or - ( - IEnumerable inputs, - Func>> mapper - ) - { - return inputs.Aggregate(False(), (predicate, input) => Or(predicate, mapper.Invoke(input))); - } -} diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs deleted file mode 100644 index 9d3ef049..00000000 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSession.cs +++ /dev/null @@ -1,195 +0,0 @@ -using EntityDb.Common.Disposables; -using EntityDb.Common.Exceptions; -using EntityDb.EntityFramework.Predicates; -using EntityDb.EntityFramework.Snapshots; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; - -namespace EntityDb.EntityFramework.Sessions; - -internal class EntityFrameworkSession : DisposableResourceBaseClass, IEntityFrameworkSession - where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : DbContext -{ - private readonly TDbContext _dbContext; - private readonly DbSet> _snapshotReferences; - private readonly DbSet _snapshots; - private readonly EntityFrameworkSnapshotSessionOptions _options; - - DbContext IEntityFrameworkSession.DbContext => _dbContext; - private IDbContextTransaction? Transaction { get; set; } - - public EntityFrameworkSession(TDbContext dbContext, EntityFrameworkSnapshotSessionOptions options) - { - _dbContext = dbContext; - _snapshotReferences = dbContext.Set>(); - _snapshots = dbContext.Set(); - _options = options; - } - - private static Expression, bool>> SnapshotPointerPredicate(Pointer snapshotPointer) - { - return snapshotReference => - snapshotReference.PointerId == snapshotPointer.Id && - snapshotReference.PointerVersionNumber == snapshotPointer.VersionNumber; - } - - private async Task ShouldDeleteSnapshot(SnapshotReference snapshotReference, CancellationToken cancellationToken) - { - if (_options.KeepSnapshotsWithoutSnapshotReferences) - { - return false; - } - - var otherSnapshotReferences = await _snapshotReferences - .Where - ( - relatedSnapshotReference => - relatedSnapshotReference.Id != snapshotReference.Id && - relatedSnapshotReference.SnapshotId == snapshotReference.SnapshotId && - relatedSnapshotReference.SnapshotVersionNumber == snapshotReference.SnapshotVersionNumber - ) - .AnyAsync(cancellationToken); - - return !otherSnapshotReferences; - } - - public async Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken) - { - AssertNotReadOnly(); - - var snapshotReferences = await _snapshotReferences - .Include(snapshotReference => snapshotReference.Snapshot) - .Where(PredicateExpressionBuilder.Or(snapshotPointers, SnapshotPointerPredicate)) - .ToArrayAsync(cancellationToken); - - foreach (var snapshotReference in snapshotReferences) - { - if (await ShouldDeleteSnapshot(snapshotReference, cancellationToken)) - { - _snapshots.Remove(snapshotReference.Snapshot); - } - - _snapshotReferences.Remove(snapshotReference); - } - - await _dbContext.SaveChangesAsync(cancellationToken); - } - - public async Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) - { - var snapshotReference = await _snapshotReferences - .Include(reference => reference.Snapshot) - .AsNoTracking() - .Where(SnapshotPointerPredicate(snapshotPointer)) - .SingleOrDefaultAsync(cancellationToken); - - return snapshotReference?.Snapshot; - } - - public async Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) - { - AssertNotReadOnly(); - - var snapshotExists = await _snapshots - .Where(snapshot.GetKeyPredicate()) - .AnyAsync(cancellationToken); - - if (!snapshotExists) - { - _snapshots.Add(snapshot); - } - - var previousSnapshotReference = await _snapshotReferences - .Include(snapshotReference => snapshotReference.Snapshot) - .Where(SnapshotPointerPredicate(snapshotPointer)) - .SingleOrDefaultAsync(cancellationToken); - - if (previousSnapshotReference != null) - { - if (await ShouldDeleteSnapshot(previousSnapshotReference, cancellationToken)) - { - _snapshots.Remove(previousSnapshotReference.Snapshot); - } - - previousSnapshotReference.SnapshotId = snapshot.GetId(); - previousSnapshotReference.SnapshotVersionNumber = snapshot.GetVersionNumber(); - previousSnapshotReference.Snapshot = snapshot; - } - else - { - _snapshotReferences.Add(new SnapshotReference - { - Id = Guid.NewGuid(), - PointerId = snapshotPointer.Id, - PointerVersionNumber = snapshotPointer.VersionNumber, - SnapshotId = snapshot.GetId(), - SnapshotVersionNumber = snapshot.GetVersionNumber(), - Snapshot = snapshot, - }); - } - - await _dbContext.SaveChangesAsync(cancellationToken); - } - - private void AssertNotReadOnly() - { - if (_options.ReadOnly) - { - throw new CannotWriteInReadOnlyModeException(); - } - } - - public static IEntityFrameworkSession Create - ( - IServiceProvider serviceProvider, - TDbContext dbContext, - EntityFrameworkSnapshotSessionOptions options - ) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, dbContext, options); - } - - public async Task StartTransaction(CancellationToken cancellationToken) - { - Transaction = await _dbContext.Database.BeginTransactionAsync(cancellationToken); - } - - [ExcludeFromCodeCoverage(Justification = - "Tests should run with the Debug configuration, and should not execute this method.")] - public async Task CommitTransaction(CancellationToken cancellationToken) - { - if (Transaction != null) - { - await Transaction.CommitAsync(cancellationToken); - await Transaction.DisposeAsync(); - - Transaction = null; - } - } - - public async Task AbortTransaction(CancellationToken cancellationToken) - { - if (Transaction != null) - { - await Transaction.RollbackAsync(cancellationToken); - await Transaction.DisposeAsync(); - - Transaction = null; - } - } - - public IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) - { - return new EntityFrameworkSession(_dbContext, snapshotSessionOptions); - } - - public override ValueTask DisposeAsync() - { - return _dbContext.DisposeAsync(); - } -} diff --git a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs b/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs deleted file mode 100644 index 26474aa9..00000000 --- a/src/EntityDb.EntityFramework/Sessions/EntityFrameworkSnapshotSessionOptions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.EntityFramework.Snapshots; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.EntityFramework.Sessions; - -/// -/// Configuration options for the EntityFramework implementation of . -/// -public sealed class EntityFrameworkSnapshotSessionOptions -{ - /// - /// This property is not used by the package. It only provides a convenient way to access - /// the connection string using IOptions, which does not appear to be a convenient thing - /// to do in vanilla Entity Framework. - /// - public string ConnectionString { get; set; } = default!; - - /// - /// If true, indicates the agent only intends to execute queries. - /// - public bool ReadOnly { get; set; } - - /// - /// If false, a snapshot will be deleted if there are no - /// records pointing to the snapshot record. - /// - /// - /// You may consider setting this to true if there are other records which reference a specific snapshot. - /// - public bool KeepSnapshotsWithoutSnapshotReferences { get; set; } - - /// - [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] - public override string ToString() - { - return $"{nameof(EntityFrameworkSnapshotSessionOptions)}"; - } -} diff --git a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs deleted file mode 100644 index d81515c0..00000000 --- a/src/EntityDb.EntityFramework/Sessions/IEntityFrameworkSession.cs +++ /dev/null @@ -1,22 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; -using Microsoft.EntityFrameworkCore; - -namespace EntityDb.EntityFramework.Sessions; - -internal interface IEntityFrameworkSession : IDisposableResource -{ - internal DbContext DbContext { get; } - - IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions); - - Task StartTransaction(CancellationToken cancellationToken); - Task CommitTransaction(CancellationToken cancellationToken); - Task AbortTransaction(CancellationToken cancellationToken); - - Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken); - - Task Get(Pointer snapshotPointer, CancellationToken cancellationToken); - - Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken); -} diff --git a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs b/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs deleted file mode 100644 index fc750f83..00000000 --- a/src/EntityDb.EntityFramework/Sessions/TestModeEntityFrameworkSession.cs +++ /dev/null @@ -1,48 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using Microsoft.EntityFrameworkCore; - -namespace EntityDb.EntityFramework.Sessions; - -internal record TestModeEntityFrameworkSession(IEntityFrameworkSession EntityFrameworkSession) : DisposableResourceBaseRecord, IEntityFrameworkSession -{ - DbContext IEntityFrameworkSession.DbContext => EntityFrameworkSession.DbContext; - - public Task StartTransaction(CancellationToken cancellationToken) - { - // Test Mode Transactions are started in the Test Mode Repository Factory - return Task.CompletedTask; - } - - public Task CommitTransaction(CancellationToken cancellationToken) - { - // Test Mode Transactions are never committed - return Task.CompletedTask; - } - - public Task AbortTransaction(CancellationToken cancellationToken) - { - // Test Mode Transactions are aborted in the Test Mode Repository Factory - return Task.CompletedTask; - } - - public Task Upsert(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken) - { - return EntityFrameworkSession.Upsert(snapshotPointer, snapshot, cancellationToken); - } - - public Task Get(Pointer snapshotPointer, CancellationToken cancellationToken) - { - return EntityFrameworkSession.Get(snapshotPointer, cancellationToken); - } - - public Task Delete(IEnumerable snapshotPointers, CancellationToken cancellationToken) - { - return EntityFrameworkSession.Delete(snapshotPointers, cancellationToken); - } - - public IEntityFrameworkSession WithSnapshotSessionOptions(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions) - { - return this with { EntityFrameworkSession = EntityFrameworkSession.WithSnapshotSessionOptions(snapshotSessionOptions) }; - } -} diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs deleted file mode 100644 index 94e7db08..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepository.cs +++ /dev/null @@ -1,61 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.EntityFramework.Sessions; - -namespace EntityDb.EntityFramework.Snapshots; - -internal class EntityFrameworkSnapshotRepository : DisposableResourceBaseClass, ISnapshotRepository -{ - private readonly IEntityFrameworkSession _entityFrameworkSession; - - public EntityFrameworkSnapshotRepository(IEntityFrameworkSession entityFrameworkSession) - { - _entityFrameworkSession = entityFrameworkSession; - } - - public async Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) - { - try - { - await _entityFrameworkSession.StartTransaction(cancellationToken); - - await _entityFrameworkSession.Delete(snapshotPointers, cancellationToken); - - await _entityFrameworkSession.CommitTransaction(cancellationToken); - - return true; - } - catch - { - await _entityFrameworkSession.AbortTransaction(cancellationToken); - - throw; - } - } - - public Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default) - { - return _entityFrameworkSession.Get(snapshotPointer, cancellationToken); - } - - public async Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, CancellationToken cancellationToken = default) - { - try - { - await _entityFrameworkSession.StartTransaction(cancellationToken); - - await _entityFrameworkSession.Upsert(snapshotPointer, snapshot, cancellationToken); - - await _entityFrameworkSession.CommitTransaction(cancellationToken); - - return true; - } - catch - { - await _entityFrameworkSession.AbortTransaction(cancellationToken); - - throw; - } - } -} diff --git a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs deleted file mode 100644 index 4c0c1254..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/EntityFrameworkSnapshotRepositoryFactory.cs +++ /dev/null @@ -1,53 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Disposables; -using EntityDb.Common.Snapshots; -using EntityDb.EntityFramework.DbContexts; -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; - -namespace EntityDb.EntityFramework.Snapshots; - -internal class EntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, - IEntityFrameworkSnapshotRepositoryFactory - where TSnapshot : class, IEntityFrameworkSnapshot - where TDbContext : DbContext, IEntityDbContext -{ - private readonly IServiceProvider _serviceProvider; - private readonly IOptionsFactory _optionsFactory; - private readonly IEntityDbContextFactory _dbContextFactory; - - public EntityFrameworkSnapshotRepositoryFactory - ( - IServiceProvider serviceProvider, - IOptionsFactory optionsFactory, - IEntityDbContextFactory dbContextFactory - ) - { - _serviceProvider = serviceProvider; - _optionsFactory = optionsFactory; - _dbContextFactory = dbContextFactory; - } - - public ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession) - { - var entityFrameworkSnapshotRepository = new EntityFrameworkSnapshotRepository - ( - entityFrameworkSession - ); - - return TryCatchSnapshotRepository.Create(_serviceProvider, entityFrameworkSnapshotRepository); - } - - public Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, CancellationToken cancellationToken) - { - var dbContext = _dbContextFactory.Create(options); - - return Task.FromResult(EntityFrameworkSession.Create(_serviceProvider, dbContext, options)); - } - - public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) - { - return _optionsFactory.Create(snapshotSessionOptionsName); - } -} diff --git a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs deleted file mode 100644 index 65a76736..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshot.cs +++ /dev/null @@ -1,17 +0,0 @@ -using EntityDb.Common.Snapshots; -using System.Linq.Expressions; - -namespace EntityDb.EntityFramework.Snapshots; - -/// -/// Indicates that a snapshot is compatible with EntityDb.EntityFramework implementations. -/// -/// The type of the snapshot -public interface IEntityFrameworkSnapshot : ISnapshot -{ - /// - /// Returns an expression of a function that can be used to check if a copy of this record already exists. - /// - /// An expression of a function that can be used to check if a copy of this record already exists. - Expression> GetKeyPredicate(); -} diff --git a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs deleted file mode 100644 index 9777116e..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/IEntityFrameworkSnapshotRepositoryFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.EntityFramework.Sessions; - -namespace EntityDb.EntityFramework.Snapshots; - -internal interface IEntityFrameworkSnapshotRepositoryFactory : ISnapshotRepositoryFactory -{ - async Task> ISnapshotRepositoryFactory.CreateRepository( - string snapshotSessionOptionsName, CancellationToken cancellationToken) - { - var options = GetSessionOptions(snapshotSessionOptionsName); - - var entityFrameworkSession = await CreateSession(options, cancellationToken); - - return CreateRepository(entityFrameworkSession); - } - - EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName); - - Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, - CancellationToken cancellationToken); - - ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession); -} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs deleted file mode 100644 index 8e8cfe47..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReference.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.EntityFramework.Snapshots; - -/// -/// Represents a unique snapshot and its pointer. -/// -/// -public sealed class SnapshotReference -{ - /// - /// Te ID of the Reference record. - /// - public required Guid Id { get; init; } - - /// - /// The ID of the Snapshot Pointer. - /// - public required Id PointerId { get; init; } - - /// - /// The Version Number of the Snapshot Pointer. - /// - public required VersionNumber PointerVersionNumber { get; init; } - - /// - /// The Id of the Snapshot. - /// - public required Id SnapshotId { get; set; } - - /// - /// The VersionNumber of the Snapshot. - /// - public required VersionNumber SnapshotVersionNumber { get; set; } - - /// - /// The Snapshot. - /// - public required TSnapshot Snapshot { get; set; } -} diff --git a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs b/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs deleted file mode 100644 index 333b19e9..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/SnapshotReferenceTypeConfiguration.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace EntityDb.EntityFramework.Snapshots; - -/// -public class SnapshotReferenceTypeConfiguration : IEntityTypeConfiguration> - where TSnapshot : class -{ - private readonly string _snapshotReferencesTableName; - - /// - /// Configure the snapshot Reference Type. - /// - /// The name of the table for snapshot references. - public SnapshotReferenceTypeConfiguration(string snapshotReferencesTableName) - { - _snapshotReferencesTableName = snapshotReferencesTableName; - } - - /// - public virtual void Configure(EntityTypeBuilder> snapshotReferenceBuilder) - { - snapshotReferenceBuilder.ToTable(_snapshotReferencesTableName); - - snapshotReferenceBuilder - .HasKey(snapshotReference => snapshotReference.Id); - - snapshotReferenceBuilder - .HasAlternateKey(snapshotReference => new - { - snapshotReference.PointerId, - snapshotReference.PointerVersionNumber - }); - - snapshotReferenceBuilder - .HasOne(snapshotReference => snapshotReference.Snapshot) - .WithMany() - .HasForeignKey(snapshotReference => new - { - snapshotReference.SnapshotId, - snapshotReference.SnapshotVersionNumber, - }) - .OnDelete(DeleteBehavior.Cascade); - } -} diff --git a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs b/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs deleted file mode 100644 index f29ca43b..00000000 --- a/src/EntityDb.EntityFramework/Snapshots/TestModeEntityFrameworkSnapshotRepositoryFactory.cs +++ /dev/null @@ -1,89 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Disposables; -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace EntityDb.EntityFramework.Snapshots; - -internal class TestModeEntityFrameworkSnapshotRepositoryFactory : DisposableResourceBaseClass, IEntityFrameworkSnapshotRepositoryFactory -{ - private readonly ILogger> _logger; - private readonly IEntityFrameworkSnapshotRepositoryFactory _entityFrameworkSnapshotRepositoryFactory; - - private (IEntityFrameworkSession Normal, TestModeEntityFrameworkSession TestMode)? _sessions; - - public TestModeEntityFrameworkSnapshotRepositoryFactory - ( - ILogger> logger, - IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory - ) - { - _logger = logger; - _entityFrameworkSnapshotRepositoryFactory = entityFrameworkSnapshotRepositoryFactory; - } - - public ISnapshotRepository CreateRepository(IEntityFrameworkSession entityFrameworkSession) - { - return _entityFrameworkSnapshotRepositoryFactory.CreateRepository(entityFrameworkSession); - } - - public async Task> CreateSession(EntityFrameworkSnapshotSessionOptions options, - CancellationToken cancellationToken) - { - if (_sessions.HasValue) - { - return _sessions.Value.TestMode - .WithSnapshotSessionOptions(options); - } - - var normalOptions = new EntityFrameworkSnapshotSessionOptions - { - ConnectionString = options.ConnectionString, - KeepSnapshotsWithoutSnapshotReferences = options.KeepSnapshotsWithoutSnapshotReferences, - }; - - var normalSession = await _entityFrameworkSnapshotRepositoryFactory.CreateSession(normalOptions, cancellationToken); - - try - { - var databaseCreator = (RelationalDatabaseCreator)normalSession.DbContext.Database.GetService(); - - await databaseCreator.CreateTablesAsync(cancellationToken); - } - catch (Exception exception) - { - _logger.LogDebug(exception, "It looks like the database tables have already been created"); - } - - var testModeSession = new TestModeEntityFrameworkSession(normalSession); - - await normalSession.StartTransaction(default); - - _sessions = (normalSession, testModeSession); - - return _sessions.Value.TestMode - .WithSnapshotSessionOptions(options); - } - - public override async ValueTask DisposeAsync() - { - if (_sessions.HasValue) - { - await _sessions.Value.Normal.AbortTransaction(default); - await _sessions.Value.Normal.DisposeAsync(); - } - } - - public EntityFrameworkSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) - { - return _entityFrameworkSnapshotRepositoryFactory.GetSessionOptions(snapshotSessionOptionsName); - } - - public static TestModeEntityFrameworkSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, IEntityFrameworkSnapshotRepositoryFactory entityFrameworkSnapshotRepositoryFactory) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, entityFrameworkSnapshotRepositoryFactory); - } -} diff --git a/src/EntityDb.EntityFramework/packages.lock.json b/src/EntityDb.EntityFramework/packages.lock.json deleted file mode 100644 index 63d19196..00000000 --- a/src/EntityDb.EntityFramework/packages.lock.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net7.0": { - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Direct", - "requested": "[7.0.10, )", - "resolved": "7.0.10", - "contentHash": "PO2QB2Du+pW210UHmepYR12bk+ZOZJCiNkA7zEAxWs+vzvrRAMsUPlDlfgX2LXE7NBsnb0uvZp7a1/qqKf3fRQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.10", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" - } - }, - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.EntityFrameworkCore": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "24NbXJqJ/x8u88/agqeb1pLdAF9+9StDLA36+P/3g5xsJPOaB2GxXn7epR8dWpZTgHsNZ7cvBMxBgfFmF+xZlg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "7.0.10", - "Microsoft.EntityFrameworkCore.Analyzers": "7.0.10", - "Microsoft.Extensions.Caching.Memory": "7.0.0", - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.Logging": "7.0.0" - } - }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "Z/lDWmGLiT9uNQrp6UXTKZxofSmAKQCiKOz98FDscTbfAGgBXE3DTTqRsPMc8HFIVVSNANSiFRz3JyLg07HN9Q==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "7.0.10", - "contentHash": "+8NVNpyJTzW6nNh/7RGfldf+mbeboVcn+X1tD8kMBCEJswuy3RqM/qecEEfOfTcWLliZExPMaHwOwtHO6RMpdA==" - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "xpidBs2KCE2gw1JrD0quHE72kvCaI3xFql5/Peb2GRtUuZX+dYPoK/NTdVMiM67Svym0M0Df9A3xyU0FbMQhHw==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==" - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "7.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Logging.Abstractions": "7.0.0", - "Microsoft.Extensions.Options": "7.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0", - "Microsoft.Extensions.Primitives": "7.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==" - }, - "entitydb.abstractions": { - "type": "Project", - "dependencies": { - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.common": { - "type": "Project", - "dependencies": { - "EntityDb.Abstractions": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/EntityDb.InMemory/EntityDb.InMemory.csproj b/src/EntityDb.InMemory/EntityDb.InMemory.csproj deleted file mode 100644 index e7245a9d..00000000 --- a/src/EntityDb.InMemory/EntityDb.InMemory.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - - EntityDb EventSourcing DDD CQRS - An implementation of the EntityDb Snapshot Repository interface, specifically for in-memory. - - - - - - - diff --git a/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index 1ab20c38..00000000 --- a/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Extensions; -using EntityDb.InMemory.Sessions; -using EntityDb.InMemory.Snapshots; -using Microsoft.Extensions.DependencyInjection; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.InMemory.Extensions; - -/// -/// Extensions for service collections. -/// -[ExcludeFromCodeCoverage(Justification = "Don't need coverage for non-test mode.")] -public static class ServiceCollectionExtensions -{ - /// - /// Adds a production-ready implementation of to a service - /// collection. - /// - /// The type of the snapshot stored in the repository. - /// The service collection. - /// Modifies the behavior of the repository to accomodate tests. - public static void AddInMemorySnapshots(this IServiceCollection serviceCollection, bool testMode = false) - { - serviceCollection.Add> - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, - _ => new InMemorySession() - ); - - serviceCollection.Add> - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient - ); - - serviceCollection.Add - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, - serviceProvider => serviceProvider - .GetRequiredService>() - .UseTestMode(testMode) - ); - } -} diff --git a/src/EntityDb.InMemory/Sessions/IInMemorySession.cs b/src/EntityDb.InMemory/Sessions/IInMemorySession.cs deleted file mode 100644 index 874ce661..00000000 --- a/src/EntityDb.InMemory/Sessions/IInMemorySession.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.InMemory.Sessions; - -internal interface IInMemorySession -{ - Task Insert(Pointer snapshotPointer, TSnapshot snapshot); - - Task Get(Pointer snapshotPointer); - - Task Delete(IEnumerable snapshotPointers); -} diff --git a/src/EntityDb.InMemory/Sessions/InMemorySession.cs b/src/EntityDb.InMemory/Sessions/InMemorySession.cs deleted file mode 100644 index ce19a751..00000000 --- a/src/EntityDb.InMemory/Sessions/InMemorySession.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Collections.Concurrent; - -namespace EntityDb.InMemory.Sessions; - -internal class InMemorySession : IInMemorySession -{ - private readonly ConcurrentDictionary _dictionary = new(); - - public async Task Insert(Pointer snapshotPointer, TSnapshot snapshot) - { - await Task.Yield(); - - return _dictionary.TryGetValue(snapshotPointer, out var previousSnapshot) - ? _dictionary.TryUpdate(snapshotPointer, snapshot, previousSnapshot) - : _dictionary.TryAdd(snapshotPointer, snapshot); - } - - public async Task Get(Pointer snapshotPointer) - { - await Task.Yield(); - - return _dictionary.GetValueOrDefault(snapshotPointer); - } - - public async Task Delete(IEnumerable snapshotPointers) - { - await Task.Yield(); - - return snapshotPointers.All(snapshotPointer => _dictionary.TryRemove(snapshotPointer, out _)); - } -} diff --git a/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs b/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs deleted file mode 100644 index 9677a053..00000000 --- a/src/EntityDb.InMemory/Sessions/InMemorySessionOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace EntityDb.InMemory.Sessions; - -/// -/// Configures the in-memory snapshot repository. -/// -public sealed class InMemorySnapshotSessionOptions -{ - /// - /// If true, indicates the agent only intends to execute queries. - /// - public bool ReadOnly { get; set; } -} diff --git a/src/EntityDb.InMemory/Sessions/ReadOnlyInMemorySession.cs b/src/EntityDb.InMemory/Sessions/ReadOnlyInMemorySession.cs deleted file mode 100644 index 5c39e06b..00000000 --- a/src/EntityDb.InMemory/Sessions/ReadOnlyInMemorySession.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Exceptions; - -namespace EntityDb.InMemory.Sessions; - -internal class ReadOnlyInMemorySession : IInMemorySession -{ - private readonly IInMemorySession _inMemorySession; - - public ReadOnlyInMemorySession(IInMemorySession inMemorySession) - { - _inMemorySession = inMemorySession; - } - - public Task Insert(Pointer snapshotPointer, TSnapshot snapshot) - { - return Task.FromException(new CannotWriteInReadOnlyModeException()); - } - - public Task Get(Pointer snapshotPointer) - { - return _inMemorySession.Get(snapshotPointer); - } - - public Task Delete(IEnumerable snapshotPointers) - { - return Task.FromException(new CannotWriteInReadOnlyModeException()); - } -} diff --git a/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs b/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs deleted file mode 100644 index 7d7132e1..00000000 --- a/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepository.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.InMemory.Sessions; - -namespace EntityDb.InMemory.Snapshots; - -internal class InMemorySnapshotRepository : DisposableResourceBaseClass, ISnapshotRepository -{ - private readonly IInMemorySession _inMemorySession; - - public InMemorySnapshotRepository(IInMemorySession inMemorySession) - { - _inMemorySession = inMemorySession; - } - - public Task PutSnapshot(Pointer subjectPointer, TSnapshot snapshot, - CancellationToken cancellationToken = default) - { - return _inMemorySession.Insert(subjectPointer, snapshot).WaitAsync(cancellationToken); - } - - public Task GetSnapshotOrDefault(Pointer snapshotPointer, CancellationToken cancellationToken = default) - { - return _inMemorySession.Get(snapshotPointer).WaitAsync(cancellationToken); - } - - public Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) - { - return _inMemorySession.Delete(snapshotPointers).WaitAsync(cancellationToken); - } -} diff --git a/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepositoryFactory.cs b/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepositoryFactory.cs deleted file mode 100644 index 38ea4924..00000000 --- a/src/EntityDb.InMemory/Snapshots/InMemorySnapshotRepositoryFactory.cs +++ /dev/null @@ -1,45 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Disposables; -using EntityDb.Common.Snapshots; -using EntityDb.InMemory.Sessions; -using Microsoft.Extensions.Options; - -namespace EntityDb.InMemory.Snapshots; - -internal class InMemorySnapshotRepositoryFactory : DisposableResourceBaseClass, - ISnapshotRepositoryFactory -{ - private readonly IInMemorySession _inMemorySession; - private readonly IOptionsFactory _optionsFactory; - private readonly IServiceProvider _serviceProvider; - - public InMemorySnapshotRepositoryFactory - ( - IServiceProvider serviceProvider, - IInMemorySession inMemorySession, - IOptionsFactory optionsFactory - ) - { - _serviceProvider = serviceProvider; - _inMemorySession = inMemorySession; - _optionsFactory = optionsFactory; - } - - public async Task> CreateRepository(string snapshotSessionOptionsName, - CancellationToken cancellationToken = default) - { - await Task.Yield(); - - var options = _optionsFactory.Create(snapshotSessionOptionsName); - - var inMemorySession = options.ReadOnly - ? new ReadOnlyInMemorySession(_inMemorySession) - : _inMemorySession; - - var inMemorySnapshotRepository = new InMemorySnapshotRepository(inMemorySession); - - cancellationToken.ThrowIfCancellationRequested(); - - return TryCatchSnapshotRepository.Create(_serviceProvider, inMemorySnapshotRepository); - } -} diff --git a/src/EntityDb.InMemory/packages.lock.json b/src/EntityDb.InMemory/packages.lock.json deleted file mode 100644 index fd75bd2f..00000000 --- a/src/EntityDb.InMemory/packages.lock.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net7.0": { - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "entitydb.abstractions": { - "type": "Project", - "dependencies": { - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.common": { - "type": "Project", - "dependencies": { - "EntityDb.Abstractions": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs b/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs index d66075b9..1c44add4 100644 --- a/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs +++ b/src/EntityDb.Json/Envelopes/JsonEnvelopeService.cs @@ -58,7 +58,7 @@ public TSerializedData Serialize(TData data) { _logger.LogError(exception, "Unable to serialize"); - throw new SerializeException(); + throw new DataSerializationException(); } } @@ -75,7 +75,7 @@ public TData Deserialize(TSerializedData serializedData) { _logger.LogError(exception, "Unable to deserialize"); - throw new DeserializeException(); + throw new DataDeserializationException(); } } diff --git a/src/EntityDb.Json/packages.lock.json b/src/EntityDb.Json/packages.lock.json index fd75bd2f..8ddaf0be 100644 --- a/src/EntityDb.Json/packages.lock.json +++ b/src/EntityDb.Json/packages.lock.json @@ -29,6 +29,35 @@ "System.Linq.Async": "[6.0.1, )" } } + }, + "net8.0": { + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } } } } \ No newline at end of file diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 86eca333..21ec9d34 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -9,11 +9,11 @@ namespace EntityDb.MongoDb.Documents; -internal sealed record AgentSignatureDocument : MessageGroupDocumentBase +internal sealed record AgentSignatureDocument : SourceDataDocumentBase { public const string CollectionName = "AgentSignatures"; - private static readonly MessageGroupSortBuilder SortBuilder = new(); + private static readonly SourceDataSortBuilder SortBuilder = new(); public static InsertDocumentsCommand GetInsertCommand ( @@ -25,13 +25,13 @@ Source source .Select(message => message.Id) .ToArray(); - var entityPointers = source.Messages - .Select(message => message.EntityPointer) + var statePointers = source.Messages + .Select(message => message.StatePointer) .Distinct() .ToArray(); - var entityIds = entityPointers - .Select(entityPointer => entityPointer.Id) + var stateIds = statePointers + .Select(statePointer => statePointer.Id) .ToArray(); var documents = new[] @@ -41,8 +41,8 @@ Source source SourceTimeStamp = source.TimeStamp, SourceId = source.Id, MessageIds = messageIds, - EntityIds = entityIds, - EntityPointers = entityPointers, + StateIds = stateIds, + StatePointers = statePointers, DataType = source.AgentSignature.GetType().Name, Data = envelopeService.Serialize(source.AgentSignature), }, @@ -57,17 +57,17 @@ Source source public static DocumentQuery GetQuery ( - IMessageGroupQuery messageGroupQuery + ISourceDataQuery sourceDataQuery ) { return new DocumentQuery - ( - CollectionName, - messageGroupQuery.GetFilter(FilterBuilder), - messageGroupQuery.GetSort(SortBuilder), - messageGroupQuery.Skip, - messageGroupQuery.Take, - messageGroupQuery.Options as MongoDbQueryOptions - ); + { + CollectionName = CollectionName, + Filter = sourceDataQuery.GetFilter(FilterBuilder), + Sort = sourceDataQuery.GetSort(SortBuilder), + Skip = sourceDataQuery.Skip, + Limit = sourceDataQuery.Take, + Options = sourceDataQuery.Options as MongoDbQueryOptions, + }; } } diff --git a/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs index 33ccf8a1..e47d747c 100644 --- a/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs @@ -5,11 +5,10 @@ namespace EntityDb.MongoDb.Documents.Commands; internal record DeleteDocumentsCommand -( - string CollectionName, - FilterDefinition FilterDefinition -) { + public required string CollectionName { get; init; } + public required FilterDefinition FilterDefinition { get; init; } + public async Task Execute(IMongoSession mongoSession, CancellationToken cancellationToken) { await mongoSession diff --git a/src/EntityDb.MongoDb/Documents/DeltaDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs similarity index 50% rename from src/EntityDb.MongoDb/Documents/DeltaDocument.cs rename to src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs index 8c48c814..185d96f4 100644 --- a/src/EntityDb.MongoDb/Documents/DeltaDocument.cs +++ b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs @@ -15,22 +15,22 @@ namespace EntityDb.MongoDb.Documents; -internal sealed record DeltaDocument : MessageDocumentBase +internal sealed record DeltaDataDocument : MessageDataDocumentBase { public const string CollectionName = "Deltas"; - private static readonly ProjectionDefinition EntityVersionProjection = + private static readonly ProjectionDefinition StateVersionProjection = ProjectionBuilder.Combine ( ProjectionBuilder.Exclude(nameof(_id)), - ProjectionBuilder.Include(nameof(EntityVersion)) + ProjectionBuilder.Include(nameof(StateVersion)) ); - private static readonly MessageFilterBuilder FilterBuilder = new(); + private static readonly MessageDataFilterBuilder DataFilterBuilder = new(); private static readonly MessageSortBuilder SortBuilder = new(); - public static InsertDocumentsCommand GetInsertCommand + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, Source source, @@ -39,55 +39,55 @@ Message message { var documents = new[] { - new DeltaDocument + new DeltaDataDocument { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, MessageId = message.Id, - EntityId = message.EntityPointer.Id, - EntityVersion = message.EntityPointer.Version, - EntityPointer = message.EntityPointer, + StateId = message.StatePointer.Id, + StateVersion = message.StatePointer.Version, + StatePointer = message.StatePointer, DataType = message.Delta.GetType().Name, Data = envelopeService.Serialize(message.Delta), }, }; - return new InsertDocumentsCommand + return new InsertDocumentsCommand ( CollectionName, documents ); } - public static DocumentQuery GetQuery + public static DocumentQuery GetQuery ( - IMessageQuery messageQuery + IMessageDataQuery messageDataQuery ) { - return new DocumentQuery - ( - CollectionName, - messageQuery.GetFilter(FilterBuilder), - messageQuery.GetSort(SortBuilder), - messageQuery.Skip, - messageQuery.Take, - messageQuery.Options as MongoDbQueryOptions - ); + return new DocumentQuery + { + CollectionName = CollectionName, + Filter = messageDataQuery.GetFilter(DataFilterBuilder), + Sort = messageDataQuery.GetSort(SortBuilder), + Skip = messageDataQuery.Skip, + Limit = messageDataQuery.Take, + Options = messageDataQuery.Options as MongoDbQueryOptions, + }; } - public static async Task GetLastEntityVersion + public static async Task GetLastStateVersion ( IMongoSession mongoSession, - Id entityId, + Id stateId, CancellationToken cancellationToken ) { - var lastEntityVersionQuery = new GetLastEntityVersionQuery(entityId); + var lastStateVersionQuery = new GetLastStateVersionQuery(stateId); - var document = await GetQuery(lastEntityVersionQuery) - .Execute(mongoSession, EntityVersionProjection, cancellationToken) + var document = await GetQuery(lastStateVersionQuery) + .Execute(mongoSession, StateVersionProjection, cancellationToken) .SingleOrDefaultAsync(cancellationToken); - return document?.EntityVersion ?? default; + return document?.StateVersion ?? default; } } diff --git a/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs index 89cdfc8d..c61b80f2 100644 --- a/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs +++ b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs @@ -61,7 +61,7 @@ public BsonDocument Serialize(TData data) { _logger.LogError(exception, "Unable to serialize"); - throw new SerializeException(); + throw new DataSerializationException(); } } @@ -78,7 +78,7 @@ public TData Deserialize(BsonDocument serializedData) { _logger.LogError(exception, "Unable to deserialize"); - throw new DeserializeException(); + throw new DataDeserializationException(); } } diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs similarity index 59% rename from src/EntityDb.MongoDb/Documents/LeaseDocument.cs rename to src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs index f723331f..93687d22 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs @@ -11,11 +11,11 @@ namespace EntityDb.MongoDb.Documents; -internal sealed record LeaseDocument : MessageDocumentBase +internal sealed record LeaseDataDocument : MessageDataDocumentBase { public const string CollectionName = "Leases"; - private static readonly LeaseFilterBuilder FilterBuilder = new(); + private static readonly LeaseDataFilterBuilder DataFilterBuilder = new(); private static readonly LeaseSortBuilder SortBuilder = new(); @@ -23,7 +23,7 @@ internal sealed record LeaseDocument : MessageDocumentBase public required string Label { get; init; } public required string Value { get; init; } - public static InsertDocumentsCommand GetInsertCommand + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, Source source, @@ -31,14 +31,14 @@ Message message ) { var leaseDocuments = message.AddLeases - .Select(lease => new LeaseDocument + .Select(lease => new LeaseDataDocument { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, MessageId = message.Id, - EntityId = message.EntityPointer.Id, - EntityVersion = message.EntityPointer.Version, - EntityPointer = message.EntityPointer, + StateId = message.StatePointer.Id, + StateVersion = message.StatePointer.Version, + StatePointer = message.StatePointer, DataType = lease.GetType().Name, Data = envelopeService.Serialize(lease), Scope = lease.Scope, @@ -47,27 +47,27 @@ Message message }) .ToArray(); - return new InsertDocumentsCommand + return new InsertDocumentsCommand ( CollectionName, leaseDocuments ); } - public static DocumentQuery GetQuery + public static DocumentQuery GetQuery ( ILeaseQuery leaseQuery ) { - return new DocumentQuery - ( - CollectionName, - leaseQuery.GetFilter(FilterBuilder), - leaseQuery.GetSort(SortBuilder), - leaseQuery.Skip, - leaseQuery.Take, - leaseQuery.Options as MongoDbQueryOptions - ); + return new DocumentQuery + { + CollectionName = CollectionName, + Filter = leaseQuery.GetFilter(DataFilterBuilder), + Sort = leaseQuery.GetSort(SortBuilder), + Skip = leaseQuery.Skip, + Limit = leaseQuery.Take, + Options = leaseQuery.Options as MongoDbQueryOptions, + }; } public static DeleteDocumentsCommand GetDeleteCommand @@ -78,9 +78,8 @@ Message message var deleteLeasesQuery = new DeleteLeasesQuery(message.DeleteLeases); return new DeleteDocumentsCommand - ( - CollectionName, - deleteLeasesQuery.GetFilter(FilterBuilder) - ); + { + CollectionName = CollectionName, FilterDefinition = deleteLeasesQuery.GetFilter(DataFilterBuilder), + }; } } diff --git a/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs new file mode 100644 index 00000000..ddb93158 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs @@ -0,0 +1,19 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Documents; +using MongoDB.Bson; +using MongoDB.Driver; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.MongoDb.Documents; + +internal abstract record MessageDataDocumentBase : DocumentBase, IMessageDataDocument +{ + public static ProjectionDefinition StatePointerProjection { get; } = + ProjectionBuilder.Include(nameof(StatePointer)); + + public required Id StateId { get; init; } + public required Version StateVersion { get; init; } + + public required Id MessageId { get; init; } + public required Pointer StatePointer { get; init; } +} diff --git a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs deleted file mode 100644 index 484af3c5..00000000 --- a/src/EntityDb.MongoDb/Documents/MessageDocumentBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Sources.Documents; -using MongoDB.Bson; -using MongoDB.Driver; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.MongoDb.Documents; - -internal abstract record MessageDocumentBase : DocumentBase, IMessageDocument -{ - public static ProjectionDefinition EntityPointerProjection { get; } = - ProjectionBuilder.Include(nameof(EntityPointer)); - - public required Id EntityId { get; init; } - public required Version EntityVersion { get; init; } - - public required Id MessageId { get; init; } - public required Pointer EntityPointer { get; init; } -} diff --git a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs deleted file mode 100644 index 2fbd6204..00000000 --- a/src/EntityDb.MongoDb/Documents/MessageGroupDocumentBase.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Sources.Documents; -using EntityDb.MongoDb.Sources.Queries.FilterBuilders; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Documents; - -internal abstract record MessageGroupDocumentBase : DocumentBase, IMessageGroupDocument -{ - protected static readonly MessageGroupFilterBuilder FilterBuilder = new(); - - public static ProjectionDefinition EntityPointersProjection { get; } = - ProjectionBuilder.Include(nameof(EntityPointers)); - - public required Id[] EntityIds { get; init; } - - public required Id[] MessageIds { get; init; } - public required Pointer[] EntityPointers { get; init; } -} diff --git a/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs index 335efdbe..38548108 100644 --- a/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs @@ -6,15 +6,14 @@ namespace EntityDb.MongoDb.Documents.Queries; internal record DocumentQuery -( - string CollectionName, - FilterDefinition Filter, - SortDefinition? Sort, - int? Skip, - int? Limit, - MongoDbQueryOptions? Options -) { + public required string CollectionName { get; init; } + public required FilterDefinition Filter { get; init; } + public required SortDefinition? Sort { get; init; } + public required int? Skip { get; init; } + public required int? Limit { get; init; } + public required MongoDbQueryOptions? Options { get; init; } + public IAsyncEnumerable Execute(IMongoSession mongoSession, ProjectionDefinition projection, CancellationToken cancellationToken) { diff --git a/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs index d7b1d362..e54fc3eb 100644 --- a/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs @@ -67,36 +67,36 @@ CancellationToken cancellationToken ); } - public static IAsyncEnumerable EnumerateMessageGroupEntityPointers + public static IAsyncEnumerable EnumerateSourceDataStatePointers ( this DocumentQuery documentQuery, IMongoSession mongoSession, CancellationToken cancellationToken ) - where TDocument : MessageGroupDocumentBase + where TDocument : SourceDataDocumentBase { return documentQuery.EnumeratePointers ( mongoSession, - MessageGroupDocumentBase.EntityPointersProjection, - documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.EntityPointers)), + SourceDataDocumentBase.StatePointersProjection, + documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.StatePointers)), cancellationToken ); } - public static IAsyncEnumerable EnumerateMessageEntityPointers + public static IAsyncEnumerable EnumerateMessageStatePointers ( this DocumentQuery documentQuery, IMongoSession mongoSession, CancellationToken cancellationToken ) - where TDocument : MessageDocumentBase + where TDocument : MessageDataDocumentBase { return documentQuery.EnumeratePointers ( mongoSession, - MessageDocumentBase.EntityPointerProjection, - documents => documents.Select(document => document.EntityPointer), + MessageDataDocumentBase.StatePointerProjection, + documents => documents.Select(document => document.StatePointer), cancellationToken ); } @@ -118,29 +118,30 @@ [EnumeratorCancellation] CancellationToken cancellationToken } } - public static IAsyncEnumerable> EnumerateEntityAnnotation + public static IAsyncEnumerable> EnumerateAnnotatedSourceData ( this DocumentQuery documentQuery, IMongoSession mongoSession, IEnvelopeService envelopeService, CancellationToken cancellationToken ) - where TDocument : MessageDocumentBase + where TDocument : MessageDataDocumentBase where TData : notnull { var documents = documentQuery.Execute(mongoSession, DocumentBase.NoIdProjection, cancellationToken); - return documents.EnumerateEntityAnnotation(envelopeService, cancellationToken); + return documents.EnumerateAnnotatedSourceData(envelopeService, + cancellationToken); } - public static IAsyncEnumerable> EnumerateEntitiesAnnotation + public static IAsyncEnumerable> EnumerateEntitiesAnnotation ( this DocumentQuery documentQuery, IMongoSession mongoSession, IEnvelopeService envelopeService, CancellationToken cancellationToken ) - where TDocument : MessageGroupDocumentBase + where TDocument : SourceDataDocumentBase where TData : notnull { var documents = documentQuery.Execute(mongoSession, DocumentBase.NoIdProjection, cancellationToken); diff --git a/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs new file mode 100644 index 00000000..7f2569e4 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Sources.Documents; +using EntityDb.MongoDb.Sources.Queries.FilterBuilders; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Documents; + +internal abstract record SourceDataDocumentBase : DocumentBase, ISourceDataDocument +{ + protected static readonly SourceDataFilterBuilder FilterBuilder = new(); + + public static ProjectionDefinition StatePointersProjection { get; } = + ProjectionBuilder.Include(nameof(StatePointers)); + + public required Id[] StateIds { get; init; } + + public required Id[] MessageIds { get; init; } + public required Pointer[] StatePointers { get; init; } +} diff --git a/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs b/src/EntityDb.MongoDb/Documents/StateDocument.cs similarity index 67% rename from src/EntityDb.MongoDb/Documents/SnapshotDocument.cs rename to src/EntityDb.MongoDb/Documents/StateDocument.cs index 51054ab5..8018b1bf 100644 --- a/src/EntityDb.MongoDb/Documents/SnapshotDocument.cs +++ b/src/EntityDb.MongoDb/Documents/StateDocument.cs @@ -5,14 +5,14 @@ namespace EntityDb.MongoDb.Documents; -internal sealed record SnapshotDocument +internal sealed record StateDocument { // ReSharper disable once InconsistentNaming // ReSharper disable once UnusedMember.Global [BsonIgnoreIfNull] public ObjectId? _id { get; init; } public required string DataType { get; init; } public required BsonDocument Data { get; init; } - public required Id SnapshotId { get; init; } - public required Version SnapshotVersion { get; init; } - public required Pointer SnapshotPointer { get; init; } + public required Id StateId { get; init; } + public required Version StateVersion { get; init; } + public required Pointer StatePointer { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs similarity index 54% rename from src/EntityDb.MongoDb/Documents/TagDocument.cs rename to src/EntityDb.MongoDb/Documents/TagDataDocument.cs index c001b56d..d04a0004 100644 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs @@ -11,18 +11,18 @@ namespace EntityDb.MongoDb.Documents; -internal sealed record TagDocument : MessageDocumentBase +internal sealed record TagDataDocument : MessageDataDocumentBase { public const string CollectionName = "Tags"; - private static readonly TagFilterBuilder FilterBuilder = new(); + private static readonly TagDataFilterBuilder DataFilterBuilder = new(); private static readonly TagSortBuilder SortBuilder = new(); public required string Label { get; init; } public required string Value { get; init; } - public static InsertDocumentsCommand GetInsertCommand + public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, Source source, @@ -30,14 +30,14 @@ Message message ) { var tagDocuments = message.AddTags - .Select(tag => new TagDocument + .Select(tag => new TagDataDocument { SourceTimeStamp = source.TimeStamp, SourceId = source.Id, MessageId = message.Id, - EntityId = message.EntityPointer.Id, - EntityVersion = message.EntityPointer.Version, - EntityPointer = message.EntityPointer, + StateId = message.StatePointer.Id, + StateVersion = message.StatePointer.Version, + StatePointer = message.StatePointer, DataType = tag.GetType().Name, Data = envelopeService.Serialize(tag), Label = tag.Label, @@ -45,27 +45,27 @@ Message message }) .ToArray(); - return new InsertDocumentsCommand + return new InsertDocumentsCommand ( CollectionName, tagDocuments ); } - public static DocumentQuery GetQuery + public static DocumentQuery GetQuery ( ITagQuery tagQuery ) { - return new DocumentQuery - ( - CollectionName, - tagQuery.GetFilter(FilterBuilder), - tagQuery.GetSort(SortBuilder), - tagQuery.Skip, - tagQuery.Take, - tagQuery.Options as MongoDbQueryOptions - ); + return new DocumentQuery + { + CollectionName = CollectionName, + Filter = tagQuery.GetFilter(DataFilterBuilder), + Sort = tagQuery.GetSort(SortBuilder), + Skip = tagQuery.Skip, + Limit = tagQuery.Take, + Options = tagQuery.Options as MongoDbQueryOptions, + }; } public static DeleteDocumentsCommand GetDeleteCommand @@ -73,12 +73,11 @@ public static DeleteDocumentsCommand GetDeleteCommand Message message ) { - var deleteTagsQuery = new DeleteTagsQuery(message.EntityPointer.Id, message.DeleteTags); + var deleteTagsQuery = new DeleteTagsQuery(message.StatePointer.Id, message.DeleteTags); return new DeleteDocumentsCommand - ( - CollectionName, - deleteTagsQuery.GetFilter(FilterBuilder) - ); + { + CollectionName = CollectionName, FilterDefinition = deleteTagsQuery.GetFilter(DataFilterBuilder), + }; } } diff --git a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj index 35f3e313..6365532d 100644 --- a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj +++ b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj @@ -1,12 +1,12 @@  - EntityDb EventSourcing DDD CQRS MongoDb - Implementations of the EntityDb ISourceRepository and ISnapshotRepository interfaces, specifically for MongoDb. + EntityDb EventSourcing EventStreaming DDD CQRS + Implementations of the EntityDb ISourceRepository and IStateRepository interfaces, specifically for MongoDb. - + diff --git a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs index 16e200d0..2e07516e 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs @@ -37,13 +37,13 @@ private static readonly IndexKeysDefinitionBuilder UniquenessConstraint ), }, - [DeltaDocument.CollectionName] = new[] + [DeltaDataDocument.CollectionName] = new[] { new CreateIndexModel ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(DeltaDocument.MessageId)) + IndexKeysBuilder.Descending(nameof(DeltaDataDocument.MessageId)) ), IdempotentConstraint ), @@ -51,46 +51,46 @@ private static readonly IndexKeysDefinitionBuilder ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(DeltaDocument.EntityId)), - IndexKeysBuilder.Descending(nameof(DeltaDocument.EntityVersion)) + IndexKeysBuilder.Descending(nameof(DeltaDataDocument.StateId)), + IndexKeysBuilder.Descending(nameof(DeltaDataDocument.StateVersion)) ), UniquenessConstraint ), }, - [LeaseDocument.CollectionName] = new[] + [LeaseDataDocument.CollectionName] = new[] { new CreateIndexModel ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(LeaseDocument.Scope)), - IndexKeysBuilder.Descending(nameof(LeaseDocument.Label)), - IndexKeysBuilder.Descending(nameof(LeaseDocument.Value)) + IndexKeysBuilder.Descending(nameof(LeaseDataDocument.Scope)), + IndexKeysBuilder.Descending(nameof(LeaseDataDocument.Label)), + IndexKeysBuilder.Descending(nameof(LeaseDataDocument.Value)) ), UniquenessConstraint ), }, - [TagDocument.CollectionName] = new[] + [TagDataDocument.CollectionName] = new[] { new CreateIndexModel ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(TagDocument.Label)), - IndexKeysBuilder.Descending(nameof(TagDocument.Value)) + IndexKeysBuilder.Descending(nameof(TagDataDocument.Label)), + IndexKeysBuilder.Descending(nameof(TagDataDocument.Value)) ), LookupIndex ), }, }; - private static readonly CreateIndexModel[] SnapshotCollection = + private static readonly CreateIndexModel[] StateCollection = { new( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(SnapshotDocument.SnapshotId)), - IndexKeysBuilder.Descending(nameof(SnapshotDocument.SnapshotVersion)) + IndexKeysBuilder.Descending(nameof(StateDocument.StateId)), + IndexKeysBuilder.Descending(nameof(StateDocument.StateVersion)) ), UniquenessConstraint ), @@ -116,11 +116,13 @@ public static async Task ProvisionSourceCollections(this IMongoClient mongoClien { var mongoCollection = mongoDatabase.GetCollection(collectionName); - var entityCollectionNameCursor = - await mongoDatabase.ListCollectionNamesAsync(cancellationToken: cancellationToken); - var entityCollectionNames = await entityCollectionNameCursor.ToListAsync(cancellationToken); + var sourceCollectionNameCursor = await mongoDatabase + .ListCollectionNamesAsync(cancellationToken: cancellationToken); - if (entityCollectionNames.Contains(collectionName)) + var sourceCollectionNames = await sourceCollectionNameCursor + .ToListAsync(cancellationToken); + + if (sourceCollectionNames.Contains(collectionName)) { continue; } @@ -143,21 +145,23 @@ public static async Task ProvisionSourceCollections(this IMongoClient mongoClien /// You should ONLY use this in your code for integration testing. Real databases should be provisioned using the /// dotnet tool EntityDb.MongoDb.Provisioner. /// - public static async Task ProvisionSnapshotCollection(this IMongoClient mongoClient, string serviceName, + public static async Task ProvisionStateCollection(this IMongoClient mongoClient, string serviceName, string collectionName, CancellationToken cancellationToken = default) { - var collectionIndices = SnapshotCollection; + var collectionIndices = StateCollection; var mongoDatabase = mongoClient.GetDatabase(serviceName); var mongoCollection = mongoDatabase.GetCollection(collectionName); - var entityCollectionNameCursor = - await mongoDatabase.ListCollectionNamesAsync(cancellationToken: cancellationToken); - var entityCollectionNames = await entityCollectionNameCursor.ToListAsync(cancellationToken); + var collectionNameCursor = await mongoDatabase + .ListCollectionNamesAsync(cancellationToken: cancellationToken); + + var collectionNames = await collectionNameCursor + .ToListAsync(cancellationToken); - if (entityCollectionNames.Contains(collectionName)) + if (collectionNames.Contains(collectionName)) { return; } diff --git a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs index b1aacb18..ab9f5bed 100644 --- a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; using EntityDb.Common.Extensions; using EntityDb.MongoDb.Documents.Envelopes; -using EntityDb.MongoDb.Snapshots; using EntityDb.MongoDb.Sources; +using EntityDb.MongoDb.States; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.MongoDb.Extensions; @@ -54,22 +54,22 @@ public static void AddMongoDbSources(this IServiceCollection serviceCollection, /// The service collection. /// Modifies the behavior of the repository to accomodate tests. /// Modifies the behavior of the repository to auto-provision collections. - public static void AddMongoDbSnapshots(this IServiceCollection serviceCollection, + public static void AddMongoDbStateRepository(this IServiceCollection serviceCollection, bool testMode = false, bool autoProvision = false) - where TSnapshot : notnull + where TState : notnull { serviceCollection.AddBsonDocumentEnvelopeService(true); - serviceCollection.Add> + serviceCollection.Add> ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient ); - serviceCollection.Add> + serviceCollection.Add> ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, serviceProvider => serviceProvider - .GetRequiredService>() + .GetRequiredService>() .UseTestMode(testMode) .UseAutoProvision(serviceProvider, autoProvision) ); diff --git a/src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs deleted file mode 100644 index ab3e90e6..00000000 --- a/src/EntityDb.MongoDb/Snapshots/IMongoDbSnapshotRepositoryFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.MongoDb.Snapshots.Sessions; - -namespace EntityDb.MongoDb.Snapshots; - -internal interface IMongoDbSnapshotRepositoryFactory : ISnapshotRepositoryFactory -{ - async Task> ISnapshotRepositoryFactory.CreateRepository( - string snapshotSessionOptionsName, CancellationToken cancellationToken) - { - var options = GetSessionOptions(snapshotSessionOptionsName); - - var mongoSession = await CreateSession(options, cancellationToken); - - return CreateRepository(mongoSession); - } - - MongoDbSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName); - - Task CreateSession(MongoDbSnapshotSessionOptions options, - CancellationToken cancellationToken); - - ISnapshotRepository CreateRepository(IMongoSession mongoSession); -} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs deleted file mode 100644 index c96b50c6..00000000 --- a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepository.cs +++ /dev/null @@ -1,82 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.Common.Envelopes; -using EntityDb.MongoDb.Documents; -using EntityDb.MongoDb.Snapshots.Sessions; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Snapshots; - -internal class MongoDbSnapshotRepository : DisposableResourceBaseClass, ISnapshotRepository - where TSnapshot : notnull -{ - private readonly IEnvelopeService _envelopeService; - private readonly IMongoSession _mongoSession; - - public MongoDbSnapshotRepository - ( - IEnvelopeService envelopeService, - IMongoSession mongoSession - ) - { - _envelopeService = envelopeService; - _mongoSession = mongoSession; - } - - public async Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, - CancellationToken cancellationToken = default) - { - try - { - _mongoSession.StartTransaction(); - - await _mongoSession.Upsert(new SnapshotDocument - { - DataType = snapshot.GetType().Name, - Data = _envelopeService.Serialize(snapshot), - SnapshotId = snapshotPointer.Id, - SnapshotVersion = snapshotPointer.Version, - SnapshotPointer = snapshotPointer, - }, cancellationToken); - - cancellationToken.ThrowIfCancellationRequested(); - - await _mongoSession.CommitTransaction(cancellationToken); - - return true; - } - catch - { - await _mongoSession.AbortTransaction(); - - throw; - } - } - - public async Task GetSnapshotOrDefault(Pointer snapshotPointer, - CancellationToken cancellationToken = default) - { - var snapshotDocument = await _mongoSession.Find(snapshotPointer, cancellationToken); - - if (snapshotDocument == null) - { - return default; - } - - return _envelopeService - .Deserialize(snapshotDocument.Data); - } - - public async Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) - { - await _mongoSession.Delete(snapshotPointers, cancellationToken); - - return true; - } - - public override async ValueTask DisposeAsync() - { - await _mongoSession.DisposeAsync(); - } -} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs deleted file mode 100644 index 7f1941ae..00000000 --- a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.MongoDb.Snapshots; - -internal static class MongoDbSnapshotRepositoryFactoryExtensions -{ - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static IMongoDbSnapshotRepositoryFactory UseTestMode( - this IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory, - bool testMode) - { - return testMode - ? new TestModeMongoDbSnapshotRepositoryFactory(mongoDbSnapshotRepositoryFactory) - : mongoDbSnapshotRepositoryFactory; - } - - public static IMongoDbSnapshotRepositoryFactory UseAutoProvision( - this IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory, - IServiceProvider serviceProvider, - bool autoProvision) - { - return autoProvision - ? AutoProvisionMongoDbSnapshotRepositoryFactory.Create(serviceProvider, - mongoDbSnapshotRepositoryFactory) - : mongoDbSnapshotRepositoryFactory; - } -} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs deleted file mode 100644 index fb63289e..00000000 --- a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactoryWrapper.cs +++ /dev/null @@ -1,41 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Disposables; -using EntityDb.MongoDb.Snapshots.Sessions; - -namespace EntityDb.MongoDb.Snapshots; - -internal abstract class MongoDbSnapshotRepositoryFactoryWrapper : DisposableResourceBaseClass, - IMongoDbSnapshotRepositoryFactory -{ - private readonly IMongoDbSnapshotRepositoryFactory _mongoDbSnapshotRepositoryFactory; - - protected MongoDbSnapshotRepositoryFactoryWrapper( - IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory) - { - _mongoDbSnapshotRepositoryFactory = mongoDbSnapshotRepositoryFactory; - } - - public virtual MongoDbSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) - { - return _mongoDbSnapshotRepositoryFactory.GetSessionOptions(snapshotSessionOptionsName); - } - - public virtual Task CreateSession(MongoDbSnapshotSessionOptions options, - CancellationToken cancellationToken) - { - return _mongoDbSnapshotRepositoryFactory.CreateSession(options, cancellationToken); - } - - public virtual ISnapshotRepository CreateRepository - ( - IMongoSession mongoSession - ) - { - return _mongoDbSnapshotRepositoryFactory.CreateRepository(mongoSession); - } - - public override async ValueTask DisposeAsync() - { - await _mongoDbSnapshotRepositoryFactory.DisposeAsync(); - } -} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs deleted file mode 100644 index ffa8367d..00000000 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/IMongoSession.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Snapshots.Sessions; - -internal interface IMongoSession : IDisposableResource -{ - IMongoDatabase MongoDatabase { get; } - string CollectionName { get; } - - Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken); - - Task Find(Pointer snapshotPointer, CancellationToken cancellationToken); - - Task Delete(Pointer[] snapshotPointer, CancellationToken cancellationToken); - - void StartTransaction(); - Task CommitTransaction(CancellationToken cancellationToken); - Task AbortTransaction(); - - IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options); -} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs deleted file mode 100644 index f9e5e7c4..00000000 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/TestModeMongoSession.cs +++ /dev/null @@ -1,54 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.MongoDb.Documents; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Snapshots.Sessions; - -internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession -{ - public string CollectionName => MongoSession.CollectionName; - - public IMongoDatabase MongoDatabase => MongoSession.MongoDatabase; - - public Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken) - { - return MongoSession.Upsert(snapshotDocument, cancellationToken); - } - - public Task Find(Pointer snapshotPointer, CancellationToken cancellationToken) - { - return MongoSession.Find - ( - snapshotPointer, - cancellationToken - ); - } - - public Task Delete(Pointer[] snapshotPointer, CancellationToken cancellationToken) - { - return MongoSession.Delete(snapshotPointer, cancellationToken); - } - - public IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options) - { - return new TestModeMongoSession(MongoSession.WithSessionOptions(options)); - } - - public void StartTransaction() - { - // Test Mode Snapshots are started in the Test Mode Repository Factory - } - - public Task CommitTransaction(CancellationToken cancellationToken) - { - // Test Mode Snapshots are never committed - return Task.CompletedTask; - } - - public Task AbortTransaction() - { - // Test Mode Snapshots are aborted in the Test Mode Repository Factory - return Task.CompletedTask; - } -} diff --git a/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs index 17f27944..0e5c4889 100644 --- a/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/IMongoDbSourceRepositoryFactory.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources; internal interface IMongoDbSourceRepositoryFactory : ISourceRepositoryFactory { - async Task ISourceRepositoryFactory.CreateRepository( + async Task ISourceRepositoryFactory.Create( string sourceSessionOptionsName, CancellationToken cancellationToken) { var options = GetSourceSessionOptions(sourceSessionOptionsName); diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs index ebc3e6c6..bb092bd4 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States.Attributes; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; @@ -29,26 +29,26 @@ IEnvelopeService envelopeService _envelopeService = envelopeService; } - public IAsyncEnumerable EnumerateSourceIds(IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(messageGroupQuery) + .GetQuery(sourceDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(IMessageQuery messageQuery, + public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return DeltaDocument - .GetQuery(messageQuery) + return DeltaDataDocument + .GetQuery(messageDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default) { - return LeaseDocument + return LeaseDataDocument .GetQuery(leaseQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } @@ -56,91 +56,92 @@ public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, CancellationToken cancellationToken = default) { - return TagDocument + return TagDataDocument .GetQuery(tagQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateEntityPointers(IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(messageGroupQuery) - .EnumerateMessageGroupEntityPointers(_mongoSession, cancellationToken); + .GetQuery(sourceDataQuery) + .EnumerateSourceDataStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateEntityPointers(IMessageQuery messageQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return DeltaDocument - .GetQuery(messageQuery) - .EnumerateMessageEntityPointers(_mongoSession, cancellationToken); + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateEntityPointers(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default) { - return LeaseDocument + return LeaseDataDocument .GetQuery(leaseQuery) - .EnumerateMessageEntityPointers(_mongoSession, cancellationToken); + .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateEntityPointers(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagQuery tagQuery, CancellationToken cancellationToken = default) { - return TagDocument + return TagDataDocument .GetQuery(tagQuery) - .EnumerateMessageEntityPointers(_mongoSession, cancellationToken); + .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateAgentSignatures(IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(messageGroupQuery) + .GetQuery(sourceDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateDeltas(IMessageQuery messageQuery, + public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return DeltaDocument - .GetQuery(messageQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, CancellationToken cancellationToken = default) { - return LeaseDocument + return LeaseDataDocument .GetQuery(leaseQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, CancellationToken cancellationToken = default) { - return TagDocument + return TagDataDocument .GetQuery(tagQuery) - .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - IMessageGroupQuery messageGroupQuery, + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(messageGroupQuery) + .GetQuery(sourceDataQuery) .EnumerateEntitiesAnnotation(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageQuery messageQuery, + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return DeltaDocument - .GetQuery(messageQuery) - .EnumerateEntityAnnotation(_mongoSession, _envelopeService, cancellationToken); + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateAnnotatedSourceData(_mongoSession, _envelopeService, cancellationToken); } public async Task Commit(Source source, @@ -158,20 +159,20 @@ public async Task Commit(Source source, cancellationToken.ThrowIfCancellationRequested(); - var previousVersion = await DeltaDocument - .GetLastEntityVersion(_mongoSession, message.EntityPointer.Id, cancellationToken); + var previousVersion = await DeltaDataDocument + .GetLastStateVersion(_mongoSession, message.StatePointer.Id, cancellationToken); - if (message.EntityPointer.Version == Version.Zero) + if (message.StatePointer.Version == Version.Zero) { currentMessage = currentMessage with { - EntityPointer = currentMessage.EntityPointer.Id + previousVersion.Next(), + StatePointer = currentMessage.StatePointer.Id + previousVersion.Next(), }; } else { OptimisticConcurrencyException.ThrowIfMismatch(previousVersion.Next(), - message.EntityPointer.Version); + message.StatePointer.Version); } await Put(source, currentMessage, cancellationToken); @@ -205,34 +206,34 @@ await AgentSignatureDocument private async Task Put(Source source, Message message, CancellationToken cancellationToken) { - await DeltaDocument + await DeltaDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); if (message.AddLeases.Length > 0) { - await LeaseDocument + await LeaseDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); } if (message.AddTags.Length > 0) { - await TagDocument + await TagDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); } if (message.DeleteLeases.Length > 0) { - await LeaseDocument + await LeaseDataDocument .GetDeleteCommand(message) .Execute(_mongoSession, cancellationToken); } if (message.DeleteTags.Length > 0) { - await TagDocument + await TagDataDocument .GetDeleteCommand(message) .Execute(_mongoSession, cancellationToken); } diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs index 55d8e615..75ac4e9b 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs @@ -66,7 +66,7 @@ IMongoSession mongoSession sourceRepository = TryCatchSourceRepository.Create(_serviceProvider, sourceRepository); sourceRepository = PublishSourceRepository.Create(_serviceProvider, sourceRepository); - + return sourceRepository; } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseDataFilterBuilder.cs similarity index 57% rename from src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs rename to src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseDataFilterBuilder.cs index ec87730b..6cc25746 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseDataFilterBuilder.cs @@ -5,21 +5,21 @@ namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal sealed class LeaseFilterBuilder : MessageFilterBuilder, - ILeaseFilterBuilder> +internal sealed class LeaseDataFilterBuilder : MessageDataFilterBuilder, + ILeaseDataFilterBuilder> { public FilterDefinition LeaseScopeEq(string scope) { - return Eq(nameof(LeaseDocument.Scope), scope); + return Eq(nameof(LeaseDataDocument.Scope), scope); } public FilterDefinition LeaseLabelEq(string label) { - return Eq(nameof(LeaseDocument.Label), label); + return Eq(nameof(LeaseDataDocument.Label), label); } public FilterDefinition LeaseValueEq(string value) { - return Eq(nameof(LeaseDocument.Value), value); + return Eq(nameof(LeaseDataDocument.Value), value); } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs new file mode 100644 index 00000000..26b432f2 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal class MessageDataFilterBuilder : FilterBuilderBase, + IMessageDataFilterBuilder> +{ + public FilterDefinition StateIdIn(params Id[] stateIds) + { + return Or + ( + stateIds + .Select(stateId => Eq(nameof(MessageDataDocumentBase.StateId), stateId)) + .ToArray() + ); + } + + public FilterDefinition StateVersionGte(Version stateVersion) + { + return Gte(nameof(MessageDataDocumentBase.StateVersion), stateVersion); + } + + public FilterDefinition StateVersionLte(Version stateVersion) + { + return Lte(nameof(MessageDataDocumentBase.StateVersion), stateVersion); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs deleted file mode 100644 index 89227f26..00000000 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageFilterBuilder.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; - -internal class MessageFilterBuilder : FilterBuilderBase, - IMessageFilterBuilder> -{ - public FilterDefinition EntityIdIn(params Id[] entityIds) - { - return Or - ( - entityIds - .Select(entityId => Eq(nameof(MessageDocumentBase.EntityId), entityId)) - .ToArray() - ); - } - - public FilterDefinition EntityVersionGte(Version entityVersion) - { - return Gte(nameof(MessageDocumentBase.EntityVersion), entityVersion); - } - - public FilterDefinition EntityVersionLte(Version entityVersion) - { - return Lte(nameof(MessageDocumentBase.EntityVersion), entityVersion); - } -} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs deleted file mode 100644 index 56ad7bae..00000000 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageGroupFilterBuilder.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; - -internal sealed class MessageGroupFilterBuilder : FilterBuilderBase, - IMessageGroupFilterBuilder> -{ - public FilterDefinition AnyEntityIdIn(params Id[] entityIds) - { - return AnyIn - ( - nameof(MessageGroupDocumentBase.EntityIds), - entityIds - ); - } -} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs new file mode 100644 index 00000000..4c51ae6d --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal sealed class SourceDataFilterBuilder : FilterBuilderBase, + ISourceDataFilterBuilder> +{ + public FilterDefinition AnyStateIdIn(params Id[] stateIds) + { + return AnyIn + ( + nameof(SourceDataDocumentBase.StateIds), + stateIds + ); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagDataFilterBuilder.cs similarity index 57% rename from src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs rename to src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagDataFilterBuilder.cs index d197975d..c8563811 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagDataFilterBuilder.cs @@ -5,16 +5,16 @@ namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal sealed class TagFilterBuilder : MessageFilterBuilder, - ITagFilterBuilder> +internal sealed class TagDataFilterBuilder : MessageDataFilterBuilder, + ITagDataFilterBuilder> { public FilterDefinition TagLabelEq(string label) { - return Eq(nameof(TagDocument.Label), label); + return Eq(nameof(TagDataDocument.Label), label); } public FilterDefinition TagValueEq(string value) { - return Eq(nameof(TagDocument.Value), value); + return Eq(nameof(TagDataDocument.Value), value); } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs index 2e607b18..0dc4d24a 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs @@ -9,16 +9,16 @@ internal sealed class LeaseSortBuilder : MessageSortBuilder, ILeaseSortBuilder LeaseScope(bool ascending) { - return Sort(ascending, nameof(LeaseDocument.Scope)); + return Sort(ascending, nameof(LeaseDataDocument.Scope)); } public SortDefinition LeaseLabel(bool ascending) { - return Sort(ascending, nameof(LeaseDocument.Label)); + return Sort(ascending, nameof(LeaseDataDocument.Label)); } public SortDefinition LeaseValue(bool ascending) { - return Sort(ascending, nameof(LeaseDocument.Value)); + return Sort(ascending, nameof(LeaseDataDocument.Value)); } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs deleted file mode 100644 index b02557c3..00000000 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageGroupSortBuilder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; - -internal sealed class MessageGroupSortBuilder : SortBuilderBase, - IMessageGroupSortBuilder> -{ - public SortDefinition EntityIds(bool ascending) - { - return Sort(ascending, nameof(AgentSignatureDocument.EntityIds)); - } -} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs index 01b99a7a..bdba2105 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs @@ -6,15 +6,15 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; internal class MessageSortBuilder : SortBuilderBase, - IMessageSortBuilder> + IMessageDataSortBuilder> { - public SortDefinition EntityId(bool ascending) + public SortDefinition StateId(bool ascending) { - return Sort(ascending, nameof(LeaseDocument.EntityId)); + return Sort(ascending, nameof(LeaseDataDocument.StateId)); } - public SortDefinition EntityVersion(bool ascending) + public SortDefinition StateVersion(bool ascending) { - return Sort(ascending, nameof(LeaseDocument.EntityVersion)); + return Sort(ascending, nameof(LeaseDataDocument.StateVersion)); } } diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs new file mode 100644 index 00000000..afed9b81 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; + +internal sealed class SourceDataSortBuilder : SortBuilderBase, + ISourceDataSortBuilder> +{ + public SortDefinition StateIds(bool ascending) + { + return Sort(ascending, nameof(AgentSignatureDocument.StateIds)); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs index 4cbae5a7..6e0622bd 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs @@ -9,11 +9,11 @@ internal sealed class TagSortBuilder : MessageSortBuilder, ITagSortBuilder TagLabel(bool ascending) { - return Sort(ascending, nameof(TagDocument.Label)); + return Sort(ascending, nameof(TagDataDocument.Label)); } public SortDefinition TagValue(bool ascending) { - return Sort(ascending, nameof(TagDocument.Value)); + return Sort(ascending, nameof(TagDataDocument.Value)); } } diff --git a/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs index 4eef75a9..19d5976c 100644 --- a/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs @@ -258,7 +258,7 @@ private void AssertNotReadOnly() { if (Options.ReadOnly) { - throw new CannotWriteInReadOnlyModeException(); + throw new ReadOnlyWriteException(); } } diff --git a/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/States/AutoProvisionMongoDbStateRepositoryFactory.cs similarity index 63% rename from src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs rename to src/EntityDb.MongoDb/States/AutoProvisionMongoDbStateRepositoryFactory.cs index 2a0c8ffc..5274bb87 100644 --- a/src/EntityDb.MongoDb/Snapshots/AutoProvisionMongoDbSnapshotRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/States/AutoProvisionMongoDbStateRepositoryFactory.cs @@ -1,12 +1,12 @@ using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Snapshots.Sessions; +using EntityDb.MongoDb.States.Sessions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace EntityDb.MongoDb.Snapshots; +namespace EntityDb.MongoDb.States; internal sealed class - AutoProvisionMongoDbSnapshotRepositoryFactory : MongoDbSnapshotRepositoryFactoryWrapper + AutoProvisionMongoDbStateRepositoryFactory : MongoDbStateRepositoryFactoryWrapper { // ReSharper disable once StaticMemberInGenericType private static readonly SemaphoreSlim Lock = new(1); @@ -14,14 +14,14 @@ internal sealed class // ReSharper disable once StaticMemberInGenericType private static bool _provisioned; - private readonly ILogger> _logger; + private readonly ILogger> _logger; - public AutoProvisionMongoDbSnapshotRepositoryFactory + public AutoProvisionMongoDbStateRepositoryFactory ( - ILogger> logger, - IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory + ILogger> logger, + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory ) - : base(mongoDbSnapshotRepositoryFactory) + : base(mongoDbStateRepositoryFactory) { _logger = logger; } @@ -44,7 +44,7 @@ private void ReleaseLock() _logger.LogInformation("MongoDb Auto-Provisioning Lock Released"); } - public override async Task CreateSession(MongoDbSnapshotSessionOptions options, + public override async Task CreateSession(MongoDbStateSessionOptions options, CancellationToken cancellationToken) { var mongoSession = await base.CreateSession(options, cancellationToken); @@ -58,7 +58,7 @@ public override async Task CreateSession(MongoDbSnapshotSessionOp return mongoSession; } - await mongoSession.MongoDatabase.Client.ProvisionSnapshotCollection + await mongoSession.MongoDatabase.Client.ProvisionStateCollection ( mongoSession.MongoDatabase.DatabaseNamespace.DatabaseName, mongoSession.CollectionName, @@ -74,11 +74,11 @@ await mongoSession.MongoDatabase.Client.ProvisionSnapshotCollection return mongoSession; } - public static IMongoDbSnapshotRepositoryFactory Create(IServiceProvider serviceProvider, - IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory) + public static IMongoDbStateRepositoryFactory Create(IServiceProvider serviceProvider, + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory) { - return ActivatorUtilities.CreateInstance>( + return ActivatorUtilities.CreateInstance>( serviceProvider, - mongoDbSnapshotRepositoryFactory); + mongoDbStateRepositoryFactory); } } diff --git a/src/EntityDb.MongoDb/States/IMongoDbStateRepositoryFactory.cs b/src/EntityDb.MongoDb/States/IMongoDbStateRepositoryFactory.cs new file mode 100644 index 00000000..704688b6 --- /dev/null +++ b/src/EntityDb.MongoDb/States/IMongoDbStateRepositoryFactory.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.States; +using EntityDb.MongoDb.States.Sessions; + +namespace EntityDb.MongoDb.States; + +internal interface IMongoDbStateRepositoryFactory : IStateRepositoryFactory +{ + async Task> IStateRepositoryFactory.Create( + string stateSessionOptionsName, CancellationToken cancellationToken) + { + var options = GetSessionOptions(stateSessionOptionsName); + + var mongoSession = await CreateSession(options, cancellationToken); + + return CreateRepository(mongoSession); + } + + MongoDbStateSessionOptions GetSessionOptions(string stateSessionOptionsName); + + Task CreateSession(MongoDbStateSessionOptions options, + CancellationToken cancellationToken); + + IStateRepository CreateRepository(IMongoSession mongoSession); +} diff --git a/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs new file mode 100644 index 00000000..19b4d446 --- /dev/null +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs @@ -0,0 +1,82 @@ +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.MongoDb.Documents; +using EntityDb.MongoDb.States.Sessions; +using MongoDB.Bson; + +namespace EntityDb.MongoDb.States; + +internal class MongoDbStateRepository : DisposableResourceBaseClass, IStateRepository + where TState : notnull +{ + private readonly IEnvelopeService _envelopeService; + private readonly IMongoSession _mongoSession; + + public MongoDbStateRepository + ( + IEnvelopeService envelopeService, + IMongoSession mongoSession + ) + { + _envelopeService = envelopeService; + _mongoSession = mongoSession; + } + + public async Task Put(Pointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + try + { + _mongoSession.StartTransaction(); + + await _mongoSession.Upsert(new StateDocument + { + DataType = state.GetType().Name, + Data = _envelopeService.Serialize(state), + StateId = statePointer.Id, + StateVersion = statePointer.Version, + StatePointer = statePointer, + }, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + await _mongoSession.CommitTransaction(cancellationToken); + + return true; + } + catch + { + await _mongoSession.AbortTransaction(); + + throw; + } + } + + public async Task Get(Pointer statePointer, + CancellationToken cancellationToken = default) + { + var stateDocument = await _mongoSession.Fetch(statePointer, cancellationToken); + + if (stateDocument == null) + { + return default; + } + + return _envelopeService + .Deserialize(stateDocument.Data); + } + + public async Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + { + await _mongoSession.Delete(statePointers, cancellationToken); + + return true; + } + + public override async ValueTask DisposeAsync() + { + await _mongoSession.DisposeAsync(); + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs similarity index 52% rename from src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs rename to src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs index d44bff21..634dfc59 100644 --- a/src/EntityDb.MongoDb/Snapshots/MongoDbSnapshotRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs @@ -1,26 +1,26 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; -using EntityDb.Common.Snapshots; -using EntityDb.MongoDb.Snapshots.Sessions; +using EntityDb.Common.States; +using EntityDb.MongoDb.States.Sessions; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Snapshots; +namespace EntityDb.MongoDb.States; -internal class MongoDbSnapshotRepositoryFactory : DisposableResourceBaseClass, - IMongoDbSnapshotRepositoryFactory - where TSnapshot : notnull +internal class MongoDbStateRepositoryFactory : DisposableResourceBaseClass, + IMongoDbStateRepositoryFactory + where TState : notnull { private readonly IEnvelopeService _envelopeService; - private readonly IOptionsFactory _optionsFactory; + private readonly IOptionsFactory _optionsFactory; private readonly IServiceProvider _serviceProvider; - public MongoDbSnapshotRepositoryFactory + public MongoDbStateRepositoryFactory ( IServiceProvider serviceProvider, - IOptionsFactory optionsFactory, + IOptionsFactory optionsFactory, IEnvelopeService envelopeService ) { @@ -29,12 +29,12 @@ IEnvelopeService envelopeService _envelopeService = envelopeService; } - public MongoDbSnapshotSessionOptions GetSessionOptions(string snapshotSessionOptionsName) + public MongoDbStateSessionOptions GetSessionOptions(string stateSessionOptionsName) { - return _optionsFactory.Create(snapshotSessionOptionsName); + return _optionsFactory.Create(stateSessionOptionsName); } - public async Task CreateSession(MongoDbSnapshotSessionOptions options, + public async Task CreateSession(MongoDbStateSessionOptions options, CancellationToken cancellationToken) { var mongoClient = new MongoClient(options.ConnectionString); @@ -54,17 +54,17 @@ await mongoClient.StartSessionAsync(new ClientSessionOptions { CausalConsistency ); } - public ISnapshotRepository CreateRepository + public IStateRepository CreateRepository ( IMongoSession mongoSession ) { - var mongoDbSnapshotRepository = new MongoDbSnapshotRepository + var mongoDbStateRepository = new MongoDbStateRepository ( _envelopeService, mongoSession ); - return TryCatchSnapshotRepository.Create(_serviceProvider, mongoDbSnapshotRepository); + return TryCatchStateRepository.Create(_serviceProvider, mongoDbStateRepository); } } diff --git a/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryExtensions.cs new file mode 100644 index 00000000..139c43dd --- /dev/null +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryExtensions.cs @@ -0,0 +1,27 @@ +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.MongoDb.States; + +internal static class MongoDbStateRepositoryFactoryExtensions +{ + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static IMongoDbStateRepositoryFactory UseTestMode( + this IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory, + bool testMode) + { + return testMode + ? new TestModeMongoDbStateRepositoryFactory(mongoDbStateRepositoryFactory) + : mongoDbStateRepositoryFactory; + } + + public static IMongoDbStateRepositoryFactory UseAutoProvision( + this IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory, + IServiceProvider serviceProvider, + bool autoProvision) + { + return autoProvision + ? AutoProvisionMongoDbStateRepositoryFactory.Create(serviceProvider, + mongoDbStateRepositoryFactory) + : mongoDbStateRepositoryFactory; + } +} diff --git a/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryWrapper.cs new file mode 100644 index 00000000..15d5a4a0 --- /dev/null +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactoryWrapper.cs @@ -0,0 +1,41 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; +using EntityDb.MongoDb.States.Sessions; + +namespace EntityDb.MongoDb.States; + +internal abstract class MongoDbStateRepositoryFactoryWrapper : DisposableResourceBaseClass, + IMongoDbStateRepositoryFactory +{ + private readonly IMongoDbStateRepositoryFactory _mongoDbStateRepositoryFactory; + + protected MongoDbStateRepositoryFactoryWrapper( + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory) + { + _mongoDbStateRepositoryFactory = mongoDbStateRepositoryFactory; + } + + public virtual MongoDbStateSessionOptions GetSessionOptions(string stateSessionOptionsName) + { + return _mongoDbStateRepositoryFactory.GetSessionOptions(stateSessionOptionsName); + } + + public virtual Task CreateSession(MongoDbStateSessionOptions options, + CancellationToken cancellationToken) + { + return _mongoDbStateRepositoryFactory.CreateSession(options, cancellationToken); + } + + public virtual IStateRepository CreateRepository + ( + IMongoSession mongoSession + ) + { + return _mongoDbStateRepositoryFactory.CreateRepository(mongoSession); + } + + public override async ValueTask DisposeAsync() + { + await _mongoDbStateRepositoryFactory.DisposeAsync(); + } +} diff --git a/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs new file mode 100644 index 00000000..0e356e45 --- /dev/null +++ b/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.MongoDb.Documents; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.States.Sessions; + +internal interface IMongoSession : IDisposableResource +{ + IMongoDatabase MongoDatabase { get; } + string CollectionName { get; } + + Task Upsert(StateDocument stateDocument, CancellationToken cancellationToken); + + Task Fetch(Pointer statePointer, CancellationToken cancellationToken); + + Task Delete(Pointer[] statePointer, CancellationToken cancellationToken); + + void StartTransaction(); + Task CommitTransaction(CancellationToken cancellationToken); + Task AbortTransaction(); + + IMongoSession WithSessionOptions(MongoDbStateSessionOptions options); +} diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs b/src/EntityDb.MongoDb/States/Sessions/MongoDbStateSessionOptions.cs similarity index 80% rename from src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs rename to src/EntityDb.MongoDb/States/Sessions/MongoDbStateSessionOptions.cs index d4862d30..9d83d369 100644 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoDbSnapshotSessionOptions.cs +++ b/src/EntityDb.MongoDb/States/Sessions/MongoDbStateSessionOptions.cs @@ -1,14 +1,14 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using EntityDb.MongoDb.Sources.Sessions; using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.MongoDb.Snapshots.Sessions; +namespace EntityDb.MongoDb.States.Sessions; /// -/// Configuration options for the MongoDb implementation of . +/// Configuration options for the MongoDb implementation of . /// -public sealed class MongoDbSnapshotSessionOptions +public sealed class MongoDbStateSessionOptions { /// /// A connection string that is compatible with @@ -16,12 +16,12 @@ public sealed class MongoDbSnapshotSessionOptions public string ConnectionString { get; set; } = default!; /// - /// The name of the database that contains the snapshots + /// The name of the database that contains the states /// public string DatabaseName { get; set; } = default!; /// - /// The name of the collection that contains the snapshots + /// The name of the collection that contains the states /// public string CollectionName { get; set; } = default!; diff --git a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs similarity index 80% rename from src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs rename to src/EntityDb.MongoDb/States/Sessions/MongoSession.cs index 5cebf7a3..e88acb73 100644 --- a/src/EntityDb.MongoDb/Snapshots/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs @@ -7,24 +7,24 @@ using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.MongoDb.Snapshots.Sessions; +namespace EntityDb.MongoDb.States.Sessions; internal record MongoSession ( ILogger Logger, IMongoDatabase MongoDatabase, IClientSessionHandle ClientSessionHandle, - MongoDbSnapshotSessionOptions Options + MongoDbStateSessionOptions Options ) : DisposableResourceBaseRecord, IMongoSession { private static readonly WriteConcern WriteConcern = WriteConcern.WMajority; - private static readonly FilterDefinitionBuilder Filter = Builders.Filter; + private static readonly FilterDefinitionBuilder Filter = Builders.Filter; private static readonly ReplaceOptions UpsertOptions = new() { IsUpsert = true }; public string CollectionName => Options.CollectionName; - public async Task Upsert(SnapshotDocument snapshotDocument, CancellationToken cancellationToken) + public async Task Upsert(StateDocument stateDocument, CancellationToken cancellationToken) { AssertNotReadOnly(); @@ -40,12 +40,12 @@ public async Task Upsert(SnapshotDocument snapshotDocument, CancellationToken ca ); await MongoDatabase - .GetCollection(Options.CollectionName) + .GetCollection(Options.CollectionName) .ReplaceOneAsync ( ClientSessionHandle, - GetFilter(snapshotDocument.SnapshotPointer), - snapshotDocument, + GetFilter(stateDocument.StatePointer), + stateDocument, UpsertOptions, cancellationToken ); @@ -60,16 +60,16 @@ await MongoDatabase ); } - public async Task Find + public async Task Fetch ( - Pointer snapshotPointer, + Pointer statePointer, CancellationToken cancellationToken ) { - var filter = GetFilter(snapshotPointer); + var filter = GetFilter(statePointer); var find = MongoDatabase - .GetCollection(Options.CollectionName) + .GetCollection(Options.CollectionName) .WithReadPreference(GetReadPreference()) .WithReadConcern(GetReadConcern()) .Find(ClientSessionHandle, filter); @@ -87,7 +87,7 @@ CancellationToken cancellationToken query ); - var snapshotDocument = await find.SingleOrDefaultAsync(cancellationToken); + var stateDocument = await find.SingleOrDefaultAsync(cancellationToken); Logger .LogInformation @@ -97,22 +97,22 @@ CancellationToken cancellationToken Options.CollectionName, serverSessionId, query, - snapshotDocument != null + stateDocument != null ); - return snapshotDocument; + return stateDocument; } - public async Task Delete(Pointer[] snapshotPointer, CancellationToken cancellationToken) + public async Task Delete(Pointer[] statePointer, CancellationToken cancellationToken) { AssertNotReadOnly(); - var filter = Filter.And(snapshotPointer.Select(GetFilter)); + var filter = Filter.And(statePointer.Select(GetFilter)); var serverSessionId = ClientSessionHandle.ServerSession.Id.ToString(); var command = MongoDatabase - .GetCollection(Options.CollectionName) + .GetCollection(Options.CollectionName) .Find(filter) .ToString()!.Replace("find", "deleteMany"); @@ -127,7 +127,7 @@ public async Task Delete(Pointer[] snapshotPointer, CancellationToken cancellati ); var deleteResult = await MongoDatabase - .GetCollection(Options.CollectionName) + .GetCollection(Options.CollectionName) .DeleteManyAsync ( ClientSessionHandle, @@ -146,7 +146,7 @@ public async Task Delete(Pointer[] snapshotPointer, CancellationToken cancellati ); } - public IMongoSession WithSessionOptions(MongoDbSnapshotSessionOptions options) + public IMongoSession WithSessionOptions(MongoDbStateSessionOptions options) { return this with { Options = options }; } @@ -209,9 +209,9 @@ public override ValueTask DisposeAsync() return base.DisposeAsync(); } - private static FilterDefinition GetFilter(Pointer snapshotPointer) + private static FilterDefinition GetFilter(Pointer statePointer) { - return Filter.Eq(document => document.SnapshotPointer, snapshotPointer); + return Filter.Eq(document => document.StatePointer, statePointer); } private ReadPreference GetReadPreference() @@ -238,7 +238,7 @@ private void AssertNotReadOnly() { if (Options.ReadOnly) { - throw new CannotWriteInReadOnlyModeException(); + throw new ReadOnlyWriteException(); } } @@ -247,7 +247,7 @@ public static IMongoSession Create IServiceProvider serviceProvider, IMongoDatabase mongoDatabase, IClientSessionHandle clientSessionHandle, - MongoDbSnapshotSessionOptions options + MongoDbStateSessionOptions options ) { return ActivatorUtilities.CreateInstance(serviceProvider, mongoDatabase, clientSessionHandle, diff --git a/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs new file mode 100644 index 00000000..20029f34 --- /dev/null +++ b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs @@ -0,0 +1,54 @@ +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.MongoDb.Documents; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.States.Sessions; + +internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession +{ + public string CollectionName => MongoSession.CollectionName; + + public IMongoDatabase MongoDatabase => MongoSession.MongoDatabase; + + public Task Upsert(StateDocument stateDocument, CancellationToken cancellationToken) + { + return MongoSession.Upsert(stateDocument, cancellationToken); + } + + public Task Fetch(Pointer statePointer, CancellationToken cancellationToken) + { + return MongoSession.Fetch + ( + statePointer, + cancellationToken + ); + } + + public Task Delete(Pointer[] statePointer, CancellationToken cancellationToken) + { + return MongoSession.Delete(statePointer, cancellationToken); + } + + public IMongoSession WithSessionOptions(MongoDbStateSessionOptions options) + { + return new TestModeMongoSession(MongoSession.WithSessionOptions(options)); + } + + public void StartTransaction() + { + // Test Mode Transactions are started in the Test Mode Repository Factory + } + + public Task CommitTransaction(CancellationToken cancellationToken) + { + // Test Mode Transactions are never committed + return Task.CompletedTask; + } + + public Task AbortTransaction() + { + // Test Mode Transactions are aborted in the Test Mode Repository Factory + return Task.CompletedTask; + } +} diff --git a/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs b/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs similarity index 66% rename from src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs rename to src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs index 54a207d7..97ce05b3 100644 --- a/src/EntityDb.MongoDb/Snapshots/TestModeMongoDbSnapshotRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs @@ -1,20 +1,20 @@ -using EntityDb.MongoDb.Snapshots.Sessions; +using EntityDb.MongoDb.States.Sessions; -namespace EntityDb.MongoDb.Snapshots; +namespace EntityDb.MongoDb.States; -internal class TestModeMongoDbSnapshotRepositoryFactory : MongoDbSnapshotRepositoryFactoryWrapper +internal class TestModeMongoDbStateRepositoryFactory : MongoDbStateRepositoryFactoryWrapper { private (IMongoSession Normal, TestModeMongoSession TestMode)? _sessions; - public TestModeMongoDbSnapshotRepositoryFactory + public TestModeMongoDbStateRepositoryFactory ( - IMongoDbSnapshotRepositoryFactory mongoDbSnapshotRepositoryFactory + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory ) - : base(mongoDbSnapshotRepositoryFactory) + : base(mongoDbStateRepositoryFactory) { } - public override async Task CreateSession(MongoDbSnapshotSessionOptions options, + public override async Task CreateSession(MongoDbStateSessionOptions options, CancellationToken cancellationToken) { if (_sessions.HasValue) @@ -23,7 +23,7 @@ public override async Task CreateSession(MongoDbSnapshotSessionOp .WithSessionOptions(options); } - var normalOptions = new MongoDbSnapshotSessionOptions + var normalOptions = new MongoDbStateSessionOptions { ConnectionString = options.ConnectionString, DatabaseName = options.DatabaseName, }; diff --git a/src/EntityDb.MongoDb/packages.lock.json b/src/EntityDb.MongoDb/packages.lock.json index 9076ded1..1c9b66b0 100644 --- a/src/EntityDb.MongoDb/packages.lock.json +++ b/src/EntityDb.MongoDb/packages.lock.json @@ -4,13 +4,13 @@ "net7.0": { "MongoDB.Driver": { "type": "Direct", - "requested": "[2.21.0, )", - "resolved": "2.21.0", - "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", + "requested": "[2.23.1, )", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", - "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0" } }, @@ -70,8 +70,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -79,18 +79,18 @@ }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", "dependencies": { "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", + "MongoDB.Bson": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { @@ -139,8 +139,163 @@ }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "MongoDB.Driver": { + "type": "Direct", + "requested": "[2.23.1, )", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "entitydb.abstractions": { "type": "Project", diff --git a/src/EntityDb.Mvc/EntityDb.Mvc.csproj b/src/EntityDb.Mvc/EntityDb.Mvc.csproj index 0ab4b098..6c61df58 100644 --- a/src/EntityDb.Mvc/EntityDb.Mvc.csproj +++ b/src/EntityDb.Mvc/EntityDb.Mvc.csproj @@ -1,7 +1,7 @@  - EntityDb EventSourcing DDD CQRS MVC + EntityDb EventSourcing EventStreaming DDD CQRS Provides a model for an AgentSignature that is mapped from an HttpContext. The source includes: Claims, Connection Information, and Raw Headers. diff --git a/src/EntityDb.Mvc/packages.lock.json b/src/EntityDb.Mvc/packages.lock.json index fd75bd2f..8ddaf0be 100644 --- a/src/EntityDb.Mvc/packages.lock.json +++ b/src/EntityDb.Mvc/packages.lock.json @@ -29,6 +29,35 @@ "System.Linq.Async": "[6.0.1, )" } } + }, + "net8.0": { + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } } } } \ No newline at end of file diff --git a/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs b/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs deleted file mode 100644 index 6104f888..00000000 --- a/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs +++ /dev/null @@ -1,283 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Npgsql.Queries; -using EntityDb.SqlDb.Converters; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Documents.Lease; -using EntityDb.SqlDb.Documents.Tag; -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Queries.Definitions.Sort; -using Npgsql; -using NpgsqlTypes; -using System.Collections; -using System.Data.Common; -using System.Text; - -namespace EntityDb.Npgsql.Converters; - -internal class NpgsqlConverter : ISqlConverter -{ - public string SqlType => "Npgsql"; - - private static string GetProjection(IDocumentReader documentReader) - { - return string.Join - ( - ", ", - documentReader.GetPropertyNames() - ); - } - - private static string GetEqFilter(EqFilterDefinition eqFilterDefinition, NpgsqlParameterCollection parameters) - { - var parameterName = AddParameter(parameters, eqFilterDefinition.PropertyName, eqFilterDefinition.PropertyValue); - - return $"{eqFilterDefinition.PropertyName} = {parameterName}"; - } - - private static string GetInFilter(InFilterDefinition inFilterDefinition, NpgsqlParameterCollection parameters) - { - var parameterNames = AddParameters(parameters, inFilterDefinition.PropertyName, inFilterDefinition.PropertyValues); - - return $"{inFilterDefinition.PropertyName} IN ({string.Join(", ", parameterNames)})"; - } - - private static string GetAnyInFilter(AnyInFilterDefinition anyInFilterDefinition, NpgsqlParameterCollection parameters) - { - var parameterName = AddParameter(parameters, anyInFilterDefinition.PropertyName, anyInFilterDefinition.PropertyValues); - - return $"{anyInFilterDefinition.PropertyName} && {parameterName}"; - } - - private static string GetGteFilter(GteFilterDefinition gteFilterDefinition, NpgsqlParameterCollection parameters) - { - var parameterName = AddParameter(parameters, gteFilterDefinition.PropertyName, gteFilterDefinition.PropertyValue); - - return $"{gteFilterDefinition.PropertyName} >= {parameterName}"; - } - - private static string GetLteFilter(LteFilterDefinition lteFilterDefinition, NpgsqlParameterCollection parameters) - { - var parameterName = AddParameter(parameters, lteFilterDefinition.PropertyName, lteFilterDefinition.PropertyValue); - - return $"{lteFilterDefinition.PropertyName} <= {parameterName}"; - } - - private static string GetFilter(IFilterDefinition filterDefinition, NpgsqlParameterCollection parameters) - { - var filter = filterDefinition switch - { - AndFilterDefinition andFilterDefinition => string.Join - ( - " AND ", - andFilterDefinition.FilterDefinitions.Select(innerFilterDefinition => GetFilter(innerFilterDefinition, parameters)) - ), - - OrFilterDefinition orFilterDefinition => string.Join - ( - " OR ", - orFilterDefinition.FilterDefinitions.Select(innerFilterDefinition => GetFilter(innerFilterDefinition, parameters)) - ), - - NotFilterDefinition notFilterDefinition => $"NOT {GetFilter(notFilterDefinition.FilterDefinition, parameters)}", - - EqFilterDefinition eqFilterDefinition => GetEqFilter(eqFilterDefinition, parameters), - - InFilterDefinition inFilterDefinition => GetInFilter(inFilterDefinition, parameters), - - AnyInFilterDefinition anyInFilterDefinition => GetAnyInFilter(anyInFilterDefinition, parameters), - - GteFilterDefinition gteFilterDefinition => GetGteFilter(gteFilterDefinition, parameters), - - LteFilterDefinition lteFilterDefinition => GetLteFilter(lteFilterDefinition, parameters), - - _ => throw new NotSupportedException() - }; - - return $"({filter})"; - } - - private static string GetCollate(string tableName, NpgsqlQueryOptions? options, string propertyName) - { - if (tableName == LeaseDocument.TableName && propertyName == nameof(LeaseDocument.Value) && options?.LeaseValueSortCollation != null) - { - return $"COLLATE {options.LeaseValueSortCollation}"; - } - - if (tableName == TagDocument.TableName && propertyName == nameof(TagDocument.Value) && options?.TagValueSortCollation != null) - { - return $"COLLATE {options.TagValueSortCollation}"; - } - - return string.Empty; - } - - private static string GetAscSort(string tableName, NpgsqlQueryOptions? options, AscSortDefinition ascSortDefinition) - { - - return $"{ascSortDefinition.PropertyName} {GetCollate(tableName, options, ascSortDefinition.PropertyName)} ASC"; - } - - private static string GetDescSort(string tableName, NpgsqlQueryOptions? options, DescSortDefinition descSortDefinition) - { - return $"{descSortDefinition.PropertyName} {GetCollate(tableName, options, descSortDefinition.PropertyName)} DESC"; - } - - private static string GetSort(string tableName, NpgsqlQueryOptions? options, ISortDefinition sortDefinition) - { - return sortDefinition switch - { - CombineSortDefinition combineSortDefinition => string.Join - ( - ", ", - combineSortDefinition.SortDefinitions.Select(innerSortDefinition => GetSort(tableName, options, innerSortDefinition)) - ), - - AscSortDefinition ascSortDefinition => GetAscSort(tableName, options, ascSortDefinition), - - DescSortDefinition descSortDefinition => GetDescSort(tableName, options, descSortDefinition), - - _ => throw new NotSupportedException() - }; - } - - private static string AddParameter(NpgsqlParameterCollection parameters, string propertyName, object propertyValue) - { -#if DEBUG - var parameterName = $"@{propertyName}_{parameters.Count}"; -#else - var parameterName = $"@{parameters.Count}"; -#endif - - var parameter = propertyValue switch - { - IEnumerable ids => new NpgsqlParameter(parameterName, NpgsqlDbType.Array | NpgsqlDbType.Uuid) - { - Value = ids - .Select(id => id.Value) - .ToArray() - }, - Id id => new NpgsqlParameter(parameterName, NpgsqlDbType.Uuid) - { - Value = id.Value - }, - TimeStamp timeStamp => new NpgsqlParameter(parameterName, NpgsqlDbType.TimestampTz) - { - Value = timeStamp.Value - }, - VersionNumber versionNumber => new NpgsqlParameter(parameterName, NpgsqlDbType.Bigint) - { - Value = Convert.ToInt64(versionNumber.Value) - }, - string value => propertyName == nameof(ITransactionDocument.Data) - ? new NpgsqlParameter(parameterName, NpgsqlDbType.Jsonb) - { - Value = value - } - : new NpgsqlParameter(parameterName, NpgsqlDbType.Varchar) - { - Value = value - }, - _ => throw new NotSupportedException() - }; - - parameters.Add(parameter); - - return parameter.ParameterName; - } - - private static IEnumerable AddParameters(NpgsqlParameterCollection parameters, string propertyName, IEnumerable propertyValues) - { - foreach (var propertyValue in propertyValues) - { - yield return AddParameter(parameters, propertyName, propertyValue); - } - } - - public DbCommand ConvertInsert - ( - string tableName, - TDocument[] documents - ) - where TDocument : ITransactionDocument - { - var dbCommand = new NpgsqlCommand(); - - var columnNames = new List(); - var parameterNameSets = new List(documents.Length); - - foreach (var index in Enumerable.Range(0, documents.Length)) - { - var document = documents[index]; - - var documentDictionary = document.ToDictionary(); - - var parameterNames = new List(documentDictionary.Count); - - foreach (var (propertyName, propertyValue) in documentDictionary) - { - var parameterName = AddParameter(dbCommand.Parameters, propertyName, propertyValue); - - parameterNames.Add(parameterName); - - if (index == 0) - { - columnNames.Add(propertyName); - } - } - - parameterNameSets.Add($"({string.Join(", ", parameterNames)})"); - } - - dbCommand.CommandText = $"INSERT INTO {tableName} ({string.Join(", ", columnNames)}) VALUES {string.Join(", ", parameterNameSets)}"; - - return dbCommand; - } - - public DbCommand ConvertQuery - ( - string tableName, - IDocumentReader documentReader, - IFilterDefinition filterDefinition, - ISortDefinition? sortDefinition, - int? skip, - int? limit, - NpgsqlQueryOptions? options - ) - { - var dbQuery = new NpgsqlCommand(); - - var sqlBuilder = new StringBuilder($"SELECT {GetProjection(documentReader)} FROM {tableName} WHERE {GetFilter(filterDefinition, dbQuery.Parameters)}"); - - if (sortDefinition != null) - { - sqlBuilder.Append($" ORDER BY {GetSort(tableName, options, sortDefinition)}"); - } - - if (skip != null) - { - sqlBuilder.Append($" OFFSET {skip.Value}"); - } - - if (limit != null) - { - sqlBuilder.Append($" LIMIT {limit.Value}"); - } - - dbQuery.CommandText = sqlBuilder.ToString(); - - return dbQuery; - } - - public DbCommand ConvertDelete - ( - string tableName, - IFilterDefinition filterDefinition - ) - { - var dbCommand = new NpgsqlCommand(); - - dbCommand.CommandText = $"DELETE FROM {tableName} WHERE {GetFilter(filterDefinition, dbCommand.Parameters)}"; - - return dbCommand; - } -} diff --git a/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj b/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj deleted file mode 100644 index 81e843d9..00000000 --- a/src/EntityDb.Npgsql/EntityDb.Npgsql.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - EntityDb EventSourcing DDD CQRS Npgsql - An implementation of the EntityDb Transaction Repository interface, specifically for Npgsql. - - - - - - - - - - - diff --git a/src/EntityDb.Npgsql/Extensions/NpgsqlConnectionExtensions.cs b/src/EntityDb.Npgsql/Extensions/NpgsqlConnectionExtensions.cs deleted file mode 100644 index e18eafde..00000000 --- a/src/EntityDb.Npgsql/Extensions/NpgsqlConnectionExtensions.cs +++ /dev/null @@ -1,93 +0,0 @@ -using EntityDb.SqlDb.Documents.AgentSignature; -using EntityDb.SqlDb.Documents.Command; -using EntityDb.SqlDb.Documents.Lease; -using EntityDb.SqlDb.Documents.Tag; -using Npgsql; - -namespace EntityDb.Npgsql.Extensions; - -/// -/// Extensions for the Npgsql Connection. -/// -public static class NpgsqlConnectionExtensions -{ - private static readonly string[] Commands = - { - "CREATE COLLATION IF NOT EXISTS numeric (provider = icu, locale = 'en-u-kn-true');", - - "CREATE extension IF NOT EXISTS \"uuid-ossp\"", - - $"CREATE TABLE IF NOT EXISTS AgentSignatures (" + - $"{nameof(AgentSignatureDocument.Id)} uuid DEFAULT uuid_generate_v4() PRIMARY KEY, " + - $"{nameof(AgentSignatureDocument.TransactionId)} uuid NOT NULL, " + - $"{nameof(AgentSignatureDocument.TransactionTimeStamp)} timestamp with time zone NOT NULL, " + - $"{nameof(AgentSignatureDocument.EntityIds)} uuid[] NOT NULL, " + - $"{nameof(AgentSignatureDocument.DataType)} varchar NOT NULL, " + - $"{nameof(AgentSignatureDocument.Data)} jsonb NOT NULL " + - $")", - - $"CREATE UNIQUE INDEX IF NOT EXISTS UniqueAgentSignatures ON AgentSignatures ({nameof(AgentSignatureDocument.TransactionId)})", - - $"CREATE TABLE IF NOT EXISTS Commands (" + - $"{nameof(CommandDocument.Id)} uuid DEFAULT uuid_generate_v4() PRIMARY KEY, " + - $"{nameof(CommandDocument.TransactionId)} uuid NOT NULL, " + - $"{nameof(CommandDocument.TransactionTimeStamp)} timestamp with time zone NOT NULL, " + - $"{nameof(CommandDocument.EntityId)} uuid NOT NULL, " + - $"{nameof(CommandDocument.EntityVersionNumber)} bigint NOT NULL, " + - $"{nameof(CommandDocument.DataType)} varchar NOT NULL, " + - $"{nameof(CommandDocument.Data)} jsonb NOT NULL " + - $")", - - $"CREATE UNIQUE INDEX IF NOT EXISTS UniqueCommands ON Commands ({nameof(CommandDocument.EntityId)}, {nameof(CommandDocument.EntityVersionNumber)})", - - $"CREATE TABLE IF NOT EXISTS Leases (" + - $"{nameof(LeaseDocument.Id)} uuid DEFAULT uuid_generate_v4() PRIMARY KEY, " + - $"{nameof(LeaseDocument.TransactionId)} uuid NOT NULL, " + - $"{nameof(LeaseDocument.TransactionTimeStamp)} timestamp with time zone NOT NULL, " + - $"{nameof(LeaseDocument.EntityId)} uuid NOT NULL, " + - $"{nameof(LeaseDocument.EntityVersionNumber)} bigint NOT NULL, " + - $"{nameof(LeaseDocument.DataType)} varchar NOT NULL, " + - $"{nameof(LeaseDocument.Data)} jsonb NOT NULL, " + - $"{nameof(LeaseDocument.Scope)} varchar NOT NULL, " + - $"{nameof(LeaseDocument.Label)} varchar NOT NULL, " + - $"{nameof(LeaseDocument.Value)} varchar NOT NULL " + - $")", - - $"CREATE UNIQUE INDEX IF NOT EXISTS UniqueLeases ON Leases ({nameof(LeaseDocument.Scope)}, {nameof(LeaseDocument.Label)}, {nameof(LeaseDocument.Value)})", - - $"CREATE TABLE IF NOT EXISTS Tags (" + - $"{nameof(TagDocument.Id)} uuid DEFAULT uuid_generate_v4() PRIMARY KEY, " + - $"{nameof(TagDocument.TransactionId)} uuid NOT NULL, " + - $"{nameof(TagDocument.TransactionTimeStamp)} timestamp with time zone NOT NULL, " + - $"{nameof(TagDocument.EntityId)} uuid NOT NULL, " + - $"{nameof(TagDocument.EntityVersionNumber)} bigint NOT NULL, " + - $"{nameof(TagDocument.DataType)} varchar NOT NULL, " + - $"{nameof(TagDocument.Data)} jsonb NOT NULL, " + - $"{nameof(TagDocument.Label)} varchar NOT NULL, " + - $"{nameof(TagDocument.Value)} varchar NOT NULL " + - $")", - - $"CREATE INDEX IF NOT EXISTS TagLookup ON Tags ({nameof(TagDocument.Label)}, {nameof(TagDocument.Value)})", - }; - - /// - /// Provisions the needed collections on the database. - /// - /// The npgsql connection. - /// A cancellation token. - /// An asynchronous task that, when complete, signals that the tables have been provisioned. - /// - /// You should ONLY use this in your code for integration testing. Real databases should be provisioned using the - /// dotnet tool EntityDb.Npgsql.Provisioner - /// - public static async Task ProvisionTables(this NpgsqlConnection npgsqlConnection, - CancellationToken cancellationToken = default) - { - foreach (var command in Commands) - { - var dbCommand = new NpgsqlCommand(command, npgsqlConnection); - - await dbCommand.ExecuteNonQueryAsync(cancellationToken); - } - } -} diff --git a/src/EntityDb.Npgsql/Extensions/NpgsqlTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.Npgsql/Extensions/NpgsqlTransactionRepositoryFactoryExtensions.cs deleted file mode 100644 index e00395a8..00000000 --- a/src/EntityDb.Npgsql/Extensions/NpgsqlTransactionRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using EntityDb.Npgsql.Queries; -using EntityDb.Npgsql.Transactions; -using EntityDb.SqlDb.Transactions; - -namespace EntityDb.Npgsql.Extensions; - -internal static class NpgsqlTransactionRepositoryFactoryExtensions -{ - public static ISqlDbTransactionRepositoryFactory UseAutoProvision( - this ISqlDbTransactionRepositoryFactory npgsqlTransactionRepositoryFactory, - IServiceProvider serviceProvider, bool autoProvision) - { - return autoProvision - ? AutoProvisionNpgsqlTransactionRepositoryFactory.Create(serviceProvider, npgsqlTransactionRepositoryFactory) - : npgsqlTransactionRepositoryFactory; - } -} diff --git a/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index 45292daa..00000000 --- a/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Extensions; -using EntityDb.Json.Envelopes; -using EntityDb.Npgsql.Converters; -using EntityDb.Npgsql.Queries; -using EntityDb.Npgsql.Transactions; -using EntityDb.SqlDb.Converters; -using EntityDb.SqlDb.Extensions; -using Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Npgsql.Extensions; - -/// -/// Extensions for service collections. -/// -public static class ServiceCollectionExtensions -{ - internal static void AddSqlDbEnvelopeService(this IServiceCollection serviceCollection) - { - serviceCollection.AddSingleton, JsonStringEnvelopeService>(); - } - - /// - /// Adds a production-ready implementation of to a service - /// collection. - /// - /// The service collection. - /// Modifies the behavior of the repository to accomodate tests. - /// Modifies the behavior of the repository to auto-provision collections. - public static void AddNpgsqlTransactions(this IServiceCollection serviceCollection, - bool testMode = false, bool autoProvision = false) - { - serviceCollection.AddSqlDbEnvelopeService(); - - serviceCollection.Add, NpgsqlConverter> - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient - ); - - serviceCollection.Add - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient - ); - - serviceCollection.Add - ( - testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, - serviceProvider => serviceProvider - .GetRequiredService() - .UseTestMode(testMode) - .UseAutoProvision(serviceProvider, autoProvision) - ); - } -} diff --git a/src/EntityDb.Npgsql/Properties/AssemblyInfo.cs b/src/EntityDb.Npgsql/Properties/AssemblyInfo.cs deleted file mode 100644 index 146bbffe..00000000 --- a/src/EntityDb.Npgsql/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -[assembly: InternalsVisibleTo("EntityDb.Provisioner")] -[assembly: InternalsVisibleTo("EntityDb.Npgsql.Tests")] diff --git a/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs b/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs deleted file mode 100644 index 37321049..00000000 --- a/src/EntityDb.Npgsql/Queries/NpgsqlQueryOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; - -namespace EntityDb.Npgsql.Queries; - -/// -/// Defines query options for the Npgsql driver. -/// -public sealed class NpgsqlQueryOptions -{ - /// - /// Defines the collation for sorting on . - /// - public string? LeaseValueSortCollation { get; set; } - - /// - /// Defines teh collation for sorting on . - /// - public string? TagValueSortCollation { get; set; } -} diff --git a/src/EntityDb.Npgsql/Transactions/AutoProvisionNpgsqlTransactionRepositoryFactory.cs b/src/EntityDb.Npgsql/Transactions/AutoProvisionNpgsqlTransactionRepositoryFactory.cs deleted file mode 100644 index 333a37c0..00000000 --- a/src/EntityDb.Npgsql/Transactions/AutoProvisionNpgsqlTransactionRepositoryFactory.cs +++ /dev/null @@ -1,83 +0,0 @@ -using EntityDb.Npgsql.Extensions; -using EntityDb.Npgsql.Queries; -using EntityDb.SqlDb.Sessions; -using EntityDb.SqlDb.Transactions; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Npgsql; - -namespace EntityDb.Npgsql.Transactions; - -internal sealed class - AutoProvisionNpgsqlTransactionRepositoryFactory : SqlDbTransactionRepositoryFactoryWrapper -{ - private static readonly SemaphoreSlim Lock = new(1); - private static bool _provisioned; - private readonly ILogger _logger; - - public AutoProvisionNpgsqlTransactionRepositoryFactory - ( - ILogger logger, - ISqlDbTransactionRepositoryFactory npgsqlTransactionRepositoryFactory) : base(npgsqlTransactionRepositoryFactory) - { - _logger = logger; - } - - private async Task AcquireLock(CancellationToken cancellationToken) - { - _logger.LogInformation("Wait for Npgsql Auto-Provisioning Lock"); - - await Lock.WaitAsync(cancellationToken); - - _logger.LogInformation("Npgsql Auto-Provisioning Lock Acquired"); - } - - private void ReleaseLock() - { - _logger.LogInformation("Release Npgsql Auto-Provisioning Lock"); - - Lock.Release(); - - _logger.LogInformation("Npgsql Auto-Provisioning Lock Released"); - } - - public override async Task> CreateSession(SqlDbTransactionSessionOptions options, - CancellationToken cancellationToken) - { - var sqlDbSession = await base.CreateSession(options, cancellationToken); - - await AcquireLock(cancellationToken); - - if (_provisioned) - { - _logger.LogInformation("Npgsql already auto-provisioned."); - - ReleaseLock(); - - return sqlDbSession; - } - - var npgsqlConnection = new NpgsqlConnection(options.ConnectionString); - - await npgsqlConnection.OpenAsync(cancellationToken); - - await npgsqlConnection.ProvisionTables(cancellationToken); - - await npgsqlConnection.CloseAsync(); - - _provisioned = true; - - _logger.LogInformation("Npgsql has been auto-provisioned"); - - ReleaseLock(); - - return sqlDbSession; - } - - public static ISqlDbTransactionRepositoryFactory Create(IServiceProvider serviceProvider, - ISqlDbTransactionRepositoryFactory npgsqlTransactionRepositoryFactory) - { - return ActivatorUtilities.CreateInstance(serviceProvider, - npgsqlTransactionRepositoryFactory); - } -} diff --git a/src/EntityDb.Npgsql/Transactions/NpgsqlTransactionRepositoryFactory.cs b/src/EntityDb.Npgsql/Transactions/NpgsqlTransactionRepositoryFactory.cs deleted file mode 100644 index 44ffcc0e..00000000 --- a/src/EntityDb.Npgsql/Transactions/NpgsqlTransactionRepositoryFactory.cs +++ /dev/null @@ -1,64 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Disposables; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Transactions; -using EntityDb.Npgsql.Queries; -using EntityDb.SqlDb.Sessions; -using EntityDb.SqlDb.Transactions; -using Microsoft.Extensions.Options; -using Npgsql; - -namespace EntityDb.Npgsql.Transactions; - -internal class NpgsqlTransactionRepositoryFactory : DisposableResourceBaseClass, ISqlDbTransactionRepositoryFactory -{ - private readonly IEnvelopeService _envelopeService; - private readonly IOptionsFactory _optionsFactory; - private readonly IServiceProvider _serviceProvider; - - public NpgsqlTransactionRepositoryFactory - ( - IServiceProvider serviceProvider, - IOptionsFactory optionsFactory, - IEnvelopeService envelopeService - ) - { - _serviceProvider = serviceProvider; - _optionsFactory = optionsFactory; - _envelopeService = envelopeService; - } - - public SqlDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName) - { - return _optionsFactory.Create(transactionSessionOptionsName); - } - - public async Task> CreateSession(SqlDbTransactionSessionOptions options, - CancellationToken cancellationToken) - { - var npgsqlConnection = new NpgsqlConnection(options.ConnectionString); - - await npgsqlConnection.OpenAsync(cancellationToken); - - return SqlDbSession.Create - ( - _serviceProvider, - npgsqlConnection, - options - ); - } - - public ITransactionRepository CreateRepository - ( - ISqlDbSession sqlDbSession - ) - { - var npgsqlTransactionRepository = new SqlDbTransactionRepository - ( - sqlDbSession, - _envelopeService - ); - - return TryCatchTransactionRepository.Create(_serviceProvider, npgsqlTransactionRepository); - } -} diff --git a/src/EntityDb.Npgsql/packages.lock.json b/src/EntityDb.Npgsql/packages.lock.json deleted file mode 100644 index 9e5972d9..00000000 --- a/src/EntityDb.Npgsql/packages.lock.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net7.0": { - "Npgsql": { - "type": "Direct", - "requested": "[7.0.4, )", - "resolved": "7.0.4", - "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" - } - }, - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" - }, - "entitydb.abstractions": { - "type": "Project", - "dependencies": { - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.common": { - "type": "Project", - "dependencies": { - "EntityDb.Abstractions": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.json": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.sqldb": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs index e6e3c729..43a4c53d 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs @@ -36,7 +36,7 @@ protected static void AddClusterNameArgument(Command command) { var clusterNameArgument = new Argument("cluster-name") { - Description = "The name of the Cluster on which the entity will be provisioned.", + Description = "The name of the Cluster on which the collections will be provisioned.", }; command.AddArgument(clusterNameArgument); diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs index 79265ee6..f4dace0f 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs @@ -32,17 +32,17 @@ private static async Task Execute(Arguments arguments) var commandResource = new MongoDbAtlasResource { - Db = arguments.ServiceName, Collection = DeltaDocument.CollectionName, + Db = arguments.ServiceName, Collection = DeltaDataDocument.CollectionName, }; var leaseResource = new MongoDbAtlasResource { - Db = arguments.ServiceName, Collection = LeaseDocument.CollectionName, + Db = arguments.ServiceName, Collection = LeaseDataDocument.CollectionName, }; var tagResources = new MongoDbAtlasResource { - Db = arguments.ServiceName, Collection = TagDocument.CollectionName, + Db = arguments.ServiceName, Collection = TagDataDocument.CollectionName, }; var roleActions = new[] diff --git a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj index a798b7e5..8dfdea37 100644 --- a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj +++ b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj @@ -7,7 +7,7 @@ true entitydb-provisioner - EntityDb EventSourcing DDD CQRS + EntityDb EventSourcing EventStreaming DDD CQRS A dotnet tool for provisioning databases. diff --git a/src/EntityDb.Provisioner/packages.lock.json b/src/EntityDb.Provisioner/packages.lock.json index 506a542b..564161be 100644 --- a/src/EntityDb.Provisioner/packages.lock.json +++ b/src/EntityDb.Provisioner/packages.lock.json @@ -54,8 +54,8 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -73,8 +73,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -82,29 +82,29 @@ }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", - "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", "dependencies": { "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", + "MongoDB.Bson": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { @@ -112,14 +112,6 @@ "resolved": "1.8.0", "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.4", - "contentHash": "7UVPYy2RP0ci04PED1tc9ZCaTw/DfSdSkLiGEFCAvwMwsgA/bAluj1liNzP1IpN0MFofnOF0cm1zJfmbEuCehg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" - } - }, "SharpCompress": { "type": "Transitive", "resolved": "0.30.1", @@ -161,8 +153,8 @@ }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "entitydb.abstractions": { "type": "Project", @@ -177,34 +169,188 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.json": { + "entitydb.mongodb": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } + } + }, + "net8.0": { + "System.CommandLine": { + "type": "Direct", + "requested": "[2.0.0-beta4.22272.1, )", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" }, - "entitydb.mongodb": { + "System.CommandLine.NamingConventionBinder": { + "type": "Direct", + "requested": "[2.0.0-beta4.22272.1, )", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "ux2eUA/syF+JtlpMDc/Lsd6PBIBuwjH3AvHnestoh5uD0WKT5b+wkQxDWVCqp9qgVjMBTLNhX19ZYFtenunt9A==", + "dependencies": { + "System.CommandLine": "2.0.0-beta4.22272.1" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "entitydb.abstractions": { "type": "Project", "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.21.0, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { + "entitydb.common": { "type": "Project", "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.4, )", + "EntityDb.Abstractions": "[1.0.0, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.sqldb": { + "entitydb.mongodb": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } } diff --git a/src/EntityDb.Redis/EntityDb.Redis.csproj b/src/EntityDb.Redis/EntityDb.Redis.csproj index 481f2c0e..a0d5b583 100644 --- a/src/EntityDb.Redis/EntityDb.Redis.csproj +++ b/src/EntityDb.Redis/EntityDb.Redis.csproj @@ -1,12 +1,12 @@  - EntityDb EventSourcing DDD CQRS Redis - An implementation of the EntityDb Snapshot Repository interface, specifically for Redis. + EntityDb EventSourcing EventStreaming DDD CQRS Redis + An implementation of the EntityDb IStateRepository interface, specifically for Redis. - + diff --git a/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs index 096b539c..19b82932 100644 --- a/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs @@ -1,9 +1,10 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using EntityDb.Common.Envelopes; using EntityDb.Common.Extensions; +using EntityDb.Common.States; using EntityDb.Json.Envelopes; using EntityDb.Redis.ConnectionMultiplexers; -using EntityDb.Redis.Snapshots; +using EntityDb.Redis.States; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; @@ -21,19 +22,19 @@ internal static void AddJsonElementEnvelopeService(this IServiceCollection servi } /// - /// Adds a production-ready implementation of to a service + /// Adds a production-ready implementation of to a service /// collection. /// - /// The type of the snapshot stored in the repository. + /// The type of the state stored in the repository. /// The service collection. /// Modifies the behavior of the repository to accomodate tests. - public static void AddRedisSnapshots(this IServiceCollection serviceCollection, bool testMode = false) + public static void AddRedisStateRepository(this IServiceCollection serviceCollection, bool testMode = false) { serviceCollection.AddJsonElementEnvelopeService(); serviceCollection.AddSingleton(); - serviceCollection.Add> + serviceCollection.Add> ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient ); @@ -42,7 +43,7 @@ public static void AddRedisSnapshots(this IServiceCollection serviceC ( testMode ? ServiceLifetime.Singleton : ServiceLifetime.Transient, serviceProvider => serviceProvider - .GetRequiredService>() + .GetRequiredService>() .UseTestMode(testMode) ); } diff --git a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs deleted file mode 100644 index c9fd9945..00000000 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs +++ /dev/null @@ -1,56 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.Common.Envelopes; -using EntityDb.Redis.Snapshots.Sessions; - -namespace EntityDb.Redis.Snapshots; - -internal class RedisSnapshotRepository : DisposableResourceBaseClass, ISnapshotRepository -{ - private readonly IEnvelopeService _envelopeService; - private readonly IRedisSession _redisSession; - - public RedisSnapshotRepository - ( - IEnvelopeService envelopeService, - IRedisSession redisSession - ) - { - _envelopeService = envelopeService; - _redisSession = redisSession; - } - - public async Task PutSnapshot(Pointer snapshotPointer, TSnapshot snapshot, - CancellationToken cancellationToken = default) - { - var snapshotValue = _envelopeService - .Serialize(snapshot); - - return await _redisSession.Insert(snapshotPointer, snapshotValue).WaitAsync(cancellationToken); - } - - public async Task GetSnapshotOrDefault(Pointer snapshotPointer, - CancellationToken cancellationToken = default) - { - var snapshotValue = await _redisSession.Find(snapshotPointer).WaitAsync(cancellationToken); - - if (!snapshotValue.HasValue) - { - return default; - } - - return _envelopeService - .Deserialize(snapshotValue!); - } - - public Task DeleteSnapshots(Pointer[] snapshotPointers, CancellationToken cancellationToken = default) - { - return _redisSession.Delete(snapshotPointers).WaitAsync(cancellationToken); - } - - public override async ValueTask DisposeAsync() - { - await _redisSession.DisposeAsync(); - } -} diff --git a/src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs b/src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs deleted file mode 100644 index cf739261..00000000 --- a/src/EntityDb.Redis/Snapshots/Sessions/IRedisSession.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; -using StackExchange.Redis; - -namespace EntityDb.Redis.Snapshots.Sessions; - -internal interface IRedisSession : IDisposableResource -{ - Task Insert(Pointer snapshotPointer, RedisValue redisValue); - Task Find(Pointer snapshotPointer); - Task Delete(Pointer[] snapshotPointers); -} diff --git a/src/EntityDb.Redis/States/RedisStateRepository.cs b/src/EntityDb.Redis/States/RedisStateRepository.cs new file mode 100644 index 00000000..2a0ab9ac --- /dev/null +++ b/src/EntityDb.Redis/States/RedisStateRepository.cs @@ -0,0 +1,56 @@ +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Redis.States.Sessions; + +namespace EntityDb.Redis.States; + +internal class RedisStateRepository : DisposableResourceBaseClass, IStateRepository +{ + private readonly IEnvelopeService _envelopeService; + private readonly IRedisSession _redisSession; + + public RedisStateRepository + ( + IEnvelopeService envelopeService, + IRedisSession redisSession + ) + { + _envelopeService = envelopeService; + _redisSession = redisSession; + } + + public async Task Put(Pointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + var stateValue = _envelopeService + .Serialize(state); + + return await _redisSession.Upsert(statePointer, stateValue).WaitAsync(cancellationToken); + } + + public async Task Get(Pointer statePointer, + CancellationToken cancellationToken = default) + { + var stateValue = await _redisSession.Fetch(statePointer).WaitAsync(cancellationToken); + + if (!stateValue.HasValue) + { + return default; + } + + return _envelopeService + .Deserialize(stateValue!); + } + + public Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + { + return _redisSession.Delete(statePointers).WaitAsync(cancellationToken); + } + + public override async ValueTask DisposeAsync() + { + await _redisSession.DisposeAsync(); + } +} diff --git a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs b/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs similarity index 56% rename from src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs rename to src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs index dd32286a..2fdbc30a 100644 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs +++ b/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs @@ -1,26 +1,26 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; -using EntityDb.Common.Snapshots; +using EntityDb.Common.States; using EntityDb.Redis.ConnectionMultiplexers; -using EntityDb.Redis.Snapshots.Sessions; +using EntityDb.Redis.States.Sessions; using Microsoft.Extensions.Options; -namespace EntityDb.Redis.Snapshots; +namespace EntityDb.Redis.States; -internal class RedisSnapshotRepositoryFactory : DisposableResourceBaseClass, - ISnapshotRepositoryFactory +internal class RedisStateRepositoryFactory : DisposableResourceBaseClass, + IStateRepositoryFactory { private readonly ConnectionMultiplexerFactory _connectionMultiplexerFactory; private readonly IEnvelopeService _envelopeService; - private readonly IOptionsFactory _optionsFactory; + private readonly IOptionsFactory _optionsFactory; private readonly IServiceProvider _serviceProvider; - public RedisSnapshotRepositoryFactory + public RedisStateRepositoryFactory ( IServiceProvider serviceProvider, ConnectionMultiplexerFactory connectionMultiplexerFactory, - IOptionsFactory optionsFactory, + IOptionsFactory optionsFactory, IEnvelopeService envelopeService ) { @@ -30,24 +30,24 @@ IEnvelopeService envelopeService _envelopeService = envelopeService; } - public async Task> CreateRepository(string snapshotSessionOptionsName, + public async Task> Create(string stateSessionOptionsName, CancellationToken cancellationToken = default) { - var options = _optionsFactory.Create(snapshotSessionOptionsName); + var options = _optionsFactory.Create(stateSessionOptionsName); var redisSession = await CreateSession(options, cancellationToken); - var redisSnapshotRepository = new RedisSnapshotRepository + var redisStateRepository = new RedisStateRepository ( _envelopeService, redisSession ); - return TryCatchSnapshotRepository.Create(_serviceProvider, redisSnapshotRepository); + return TryCatchStateRepository.Create(_serviceProvider, redisStateRepository); } - private async Task CreateSession(RedisSnapshotSessionOptions options, + private async Task CreateSession(RedisStateSessionOptions options, CancellationToken cancellationToken) { var connectionMultiplexer = diff --git a/src/EntityDb.Redis/States/Sessions/IRedisSession.cs b/src/EntityDb.Redis/States/Sessions/IRedisSession.cs new file mode 100644 index 00000000..ede98cbe --- /dev/null +++ b/src/EntityDb.Redis/States/Sessions/IRedisSession.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.ValueObjects; +using StackExchange.Redis; + +namespace EntityDb.Redis.States.Sessions; + +internal interface IRedisSession : IDisposableResource +{ + Task Upsert(Pointer statePointer, RedisValue redisValue); + Task Fetch(Pointer statePointer); + Task Delete(Pointer[] statePointers); +} diff --git a/src/EntityDb.Redis/Snapshots/Sessions/RedisSession.cs b/src/EntityDb.Redis/States/Sessions/RedisSession.cs similarity index 70% rename from src/EntityDb.Redis/Snapshots/Sessions/RedisSession.cs rename to src/EntityDb.Redis/States/Sessions/RedisSession.cs index 7a92f7ac..426a5c2d 100644 --- a/src/EntityDb.Redis/Snapshots/Sessions/RedisSession.cs +++ b/src/EntityDb.Redis/States/Sessions/RedisSession.cs @@ -5,20 +5,20 @@ using Microsoft.Extensions.Logging; using StackExchange.Redis; -namespace EntityDb.Redis.Snapshots.Sessions; +namespace EntityDb.Redis.States.Sessions; internal sealed record RedisSession ( ILogger Logger, IDatabase Database, - RedisSnapshotSessionOptions Options + RedisStateSessionOptions Options ) : DisposableResourceBaseRecord, IRedisSession { - public async Task Insert(Pointer snapshotPointer, RedisValue redisValue) + public async Task Upsert(Pointer statePointer, RedisValue redisValue) { AssertNotReadOnly(); - var redisKey = GetSnapshotKey(snapshotPointer); + var redisKey = GetRedisKey(statePointer); Logger .LogInformation @@ -34,23 +34,23 @@ public async Task Insert(Pointer snapshotPointer, RedisValue redisValue) await redisTransaction.ExecuteAsync(GetCommandFlags()); - var inserted = await insertedTask; + var upserted = await insertedTask; Logger .LogInformation ( - "Finished Running Redis Insert on `{DatabaseIndex}.{RedisKey}`\n\nCommitted: {Committed}", + "Finished Running Redis Insert on `{DatabaseIndex}.{RedisKey}`\n\nUpserted: {Upserted}", Database.Database, redisKey.ToString(), - inserted + upserted ); - return inserted; + return upserted; } - public async Task Find(Pointer snapshotPointer) + public async Task Fetch(Pointer statePointer) { - var redisKey = GetSnapshotKey(snapshotPointer); + var redisKey = GetRedisKey(statePointer); Logger .LogInformation @@ -74,7 +74,7 @@ public async Task Find(Pointer snapshotPointer) return redisValue; } - public async Task Delete(Pointer[] snapshotPointers) + public async Task Delete(Pointer[] statePointers) { AssertNotReadOnly(); @@ -83,27 +83,27 @@ public async Task Delete(Pointer[] snapshotPointers) ( "Started Running Redis Delete on `{DatabaseIndex}` for {NumberOfKeys} Key(s)", Database.Database, - snapshotPointers.Length + statePointers.Length ); var redisTransaction = Database.CreateTransaction(); - var deleteSnapshotTasks = snapshotPointers - .Select(snapshotPointer => redisTransaction.KeyDeleteAsync(GetSnapshotKey(snapshotPointer))) + var deleteStateTasks = statePointers + .Select(statePointer => redisTransaction.KeyDeleteAsync(GetRedisKey(statePointer))) .ToArray(); await redisTransaction.ExecuteAsync(GetCommandFlags()); - await Task.WhenAll(deleteSnapshotTasks); + await Task.WhenAll(deleteStateTasks); - var allDeleted = deleteSnapshotTasks.All(task => task.Result); + var allDeleted = deleteStateTasks.All(task => task.Result); Logger .LogInformation ( "Finished Running Redis Delete on `{DatabaseIndex}` for {NumberOfKeys} Key(s)\n\nAll Deleted: {AllDeleted}", Database.Database, - snapshotPointers.Length, + statePointers.Length, allDeleted ); @@ -126,20 +126,20 @@ private void AssertNotReadOnly() { if (Options.ReadOnly) { - throw new CannotWriteInReadOnlyModeException(); + throw new ReadOnlyWriteException(); } } - private RedisKey GetSnapshotKey(Pointer snapshotPointer) + private RedisKey GetRedisKey(Pointer statePointer) { - return $"{Options.KeyNamespace}#{snapshotPointer}"; + return $"{Options.KeyNamespace}#{statePointer}"; } public static IRedisSession Create ( IServiceProvider serviceProvider, IDatabase database, - RedisSnapshotSessionOptions options + RedisStateSessionOptions options ) { return ActivatorUtilities.CreateInstance(serviceProvider, database, options); diff --git a/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs b/src/EntityDb.Redis/States/Sessions/RedisStateSessionOptions.cs similarity index 70% rename from src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs rename to src/EntityDb.Redis/States/Sessions/RedisStateSessionOptions.cs index 28366608..a3fe8717 100644 --- a/src/EntityDb.Redis/Snapshots/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/States/Sessions/RedisStateSessionOptions.cs @@ -1,13 +1,13 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using StackExchange.Redis; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.Redis.Snapshots.Sessions; +namespace EntityDb.Redis.States.Sessions; /// -/// Configuration options for the Redis implementation of . +/// Configuration options for the Redis implementation of . /// -public sealed class RedisSnapshotSessionOptions +public sealed class RedisStateSessionOptions { /// /// A connection string that is compatible with @@ -15,8 +15,8 @@ public sealed class RedisSnapshotSessionOptions public string ConnectionString { get; set; } = default!; /// - /// Choose a key namespace for snapshots. Snapshots are stored with keys in the following format: - /// {KeyNamespace}#{SnapshotId}@{SnapshotVersion} + /// Choose a key namespace for states. States are stored with keys in the following format: + /// {KeyNamespace}#{StateId}@{StateVersion} /// public string KeyNamespace { get; set; } = default!; @@ -34,6 +34,6 @@ public sealed class RedisSnapshotSessionOptions [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { - return $"{nameof(RedisSnapshotSessionOptions)}"; + return $"{nameof(RedisStateSessionOptions)}"; } } diff --git a/src/EntityDb.Redis/packages.lock.json b/src/EntityDb.Redis/packages.lock.json index 5ca05b22..26ed44de 100644 --- a/src/EntityDb.Redis/packages.lock.json +++ b/src/EntityDb.Redis/packages.lock.json @@ -4,10 +4,11 @@ "net7.0": { "StackExchange.Redis": { "type": "Direct", - "requested": "[2.6.122, )", - "resolved": "2.6.122", - "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", + "requested": "[2.7.10, )", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", "Pipelines.Sockets.Unofficial": "2.2.8" } }, @@ -25,6 +26,75 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", + "dependencies": { + "System.IO.Pipelines": "5.0.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "StackExchange.Redis": { + "type": "Direct", + "requested": "[2.7.10, )", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", diff --git a/src/EntityDb.SqlDb/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.SqlDb/Commands/DeleteDocumentsCommand.cs deleted file mode 100644 index 3cfadf7f..00000000 --- a/src/EntityDb.SqlDb/Commands/DeleteDocumentsCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Commands; - -internal record DeleteDocumentsCommand -( - string TableName, - IFilterDefinition FilterDefinition -) -{ - public async Task Execute(ISqlDbSession sqlDbSession, CancellationToken cancellationToken) - where TOptions : class - { - await sqlDbSession - .Delete(TableName, FilterDefinition, cancellationToken); - } -} diff --git a/src/EntityDb.SqlDb/Commands/InsertDocumentsCommand.cs b/src/EntityDb.SqlDb/Commands/InsertDocumentsCommand.cs deleted file mode 100644 index 66962216..00000000 --- a/src/EntityDb.SqlDb/Commands/InsertDocumentsCommand.cs +++ /dev/null @@ -1,19 +0,0 @@ -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Commands; - -internal record InsertDocumentsCommand -( - string TableName, - TDocument[] Documents -) - where TDocument : ITransactionDocument -{ - public async Task Execute(ISqlDbSession sqlDbSession, CancellationToken cancellationToken) - where TOptions : class - { - await sqlDbSession - .Insert(TableName, Documents, cancellationToken); - } -} diff --git a/src/EntityDb.SqlDb/Converters/ISqlConverter.cs b/src/EntityDb.SqlDb/Converters/ISqlConverter.cs deleted file mode 100644 index a6b1cb15..00000000 --- a/src/EntityDb.SqlDb/Converters/ISqlConverter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Queries.Definitions.Sort; -using System.Data.Common; - -namespace EntityDb.SqlDb.Converters; - -internal interface ISqlConverter -{ - string SqlType { get; } - - DbCommand ConvertInsert(string tableName, TDocument[] documents) where TDocument : ITransactionDocument; - - DbCommand ConvertQuery - ( - string tableName, - IDocumentReader documentReader, - IFilterDefinition filterDefinition, - ISortDefinition? sortDefinition, - int? skip, - int? limit, - TOptions? options - ); - - DbCommand ConvertDelete - ( - string tableName, - IFilterDefinition filterDefinition - ); -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs deleted file mode 100644 index ea78aaf6..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDataDocumentReader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.AgentSignature; - -internal class AgentSignatureDataDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(AgentSignatureDocument.Data), - }; - - public AgentSignatureDataDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new AgentSignatureDocument - { - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs deleted file mode 100644 index a2ae6728..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs +++ /dev/null @@ -1,90 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.SqlDb.Commands; -using EntityDb.SqlDb.Queries; -using EntityDb.SqlDb.Queries.FilterBuilders; -using EntityDb.SqlDb.Queries.SortBuilders; - -namespace EntityDb.SqlDb.Documents.AgentSignature; - -internal sealed record AgentSignatureDocument : DocumentBase, IEntitiesDocument -{ - public static string TableName => "AgentSignatures"; - - private static readonly AgentSignatureFilterBuilder FilterBuilder = new(); - - private static readonly AgentSignatureSortBuilder SortBuilder = new(); - - public Id[] EntityIds { get; init; } = Array.Empty(); - public Pointer[] EntityPointers { get; init; } = Array.Empty(); - - public Id[] GetSubjectIds() => EntityIds; - public Pointer[] GetSubjectPointers() => EntityPointers; - - public static IDocumentReader DocumentReader { get; } = new AgentSignatureDocumentReader(); - - public static IDocumentReader TransactionIdDocumentReader { get; } = new AgentSignatureTransactionIdDocumentReader(); - - public static IDocumentReader DataDocumentReader { get; } = new AgentSignatureDataDocumentReader(); - - public static IDocumentReader EntityIdsDocumentReader { get; } = new AgentSignatureEntityIdsDocumentReader(); - - public static InsertDocumentsCommand GetInsert - ( - IEnvelopeService envelopeService, - ITransaction transaction - ) - { - return new InsertDocumentsCommand - ( - TableName, - new[] - { - new AgentSignatureDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityIds = transaction.Subjects - .Select(transactionCommand => transactionCommand.SubjectId) - .Distinct() - .ToArray(), - EntityPointers = transaction.Subjects - .Select(transactionCommand => transactionCommand.SubjectId + transactionCommand.SubjectVersionNumber) - .Distinct() - .ToArray(), - DataType = transaction.AgentSignature.GetType().Name, - Data = envelopeService.Serialize(transaction.AgentSignature), - } - } - ); - } - - public static DocumentQuery GetQuery - ( - IAgentSignatureQuery agentSignatureQuery - ) - { - return new DocumentQuery - ( - agentSignatureQuery.GetFilter(FilterBuilder), - agentSignatureQuery.GetSort(SortBuilder), - agentSignatureQuery.Skip, - agentSignatureQuery.Take, - agentSignatureQuery.Options - ); - } - - public Dictionary ToDictionary() - { - return new Dictionary - { - [nameof(TransactionId)] = TransactionId, - [nameof(TransactionTimeStamp)] = TransactionTimeStamp, - [nameof(EntityIds)] = EntityIds, - [nameof(DataType)] = DataType, - [nameof(Data)] = Data, - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs deleted file mode 100644 index 65cbb829..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.AgentSignature; - -internal class AgentSignatureDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(AgentSignatureDocument.TransactionId), - nameof(AgentSignatureDocument.TransactionTimeStamp), - nameof(AgentSignatureDocument.EntityIds), - nameof(AgentSignatureDocument.DataType), - nameof(AgentSignatureDocument.Data), - }; - - public AgentSignatureDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new AgentSignatureDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), - EntityIds = (await dbDataReader.GetFieldValueAsync(EntityIdsOrdinal, cancellationToken)) - .Select(guid => new Id(guid)) - .ToArray(), - DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs deleted file mode 100644 index c49d3f30..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace EntityDb.SqlDb.Documents.AgentSignature; - -internal abstract class AgentSignatureDocumentReaderBase -{ - private readonly string[] _propertyNames; - - protected readonly int TransactionIdOrdinal; - protected readonly int TransactionTimeStampOrdinal; - protected readonly int EntityIdsOrdinal; - protected readonly int DataTypeOrdinal; - protected readonly int DataOrdinal; - - public string[] GetPropertyNames() => _propertyNames; - - protected AgentSignatureDocumentReaderBase(string[] propertyNames) - { - _propertyNames = propertyNames; - - TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.TransactionId)); - TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.TransactionTimeStamp)); - EntityIdsOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.EntityIds)); - DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.DataType)); - DataOrdinal = Array.IndexOf(_propertyNames, nameof(AgentSignatureDocument.Data)); - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs deleted file mode 100644 index 184e0e9e..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureEntityIdsDocumentReader.cs +++ /dev/null @@ -1,26 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.AgentSignature; - -internal class AgentSignatureEntityIdsDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(AgentSignatureDocument.EntityIds), - }; - - public AgentSignatureEntityIdsDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new AgentSignatureDocument - { - EntityIds = (await dbDataReader.GetFieldValueAsync(EntityIdsOrdinal, cancellationToken)) - .Select(guid => new Id(guid)) - .ToArray() - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs deleted file mode 100644 index 33d91bb7..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureTransactionIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.AgentSignature; - -internal class AgentSignatureTransactionIdDocumentReader : AgentSignatureDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(AgentSignatureDocument.TransactionId), - }; - - public AgentSignatureTransactionIdDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new AgentSignatureDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs deleted file mode 100644 index 374b5584..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Command; - -internal class CommandDataDocumentReader : CommandDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(CommandDocument.Data), - }; - - public CommandDataDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new CommandDocument - { - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs deleted file mode 100644 index 2d543720..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs +++ /dev/null @@ -1,109 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Queries; -using EntityDb.SqlDb.Commands; -using EntityDb.SqlDb.Queries; -using EntityDb.SqlDb.Queries.FilterBuilders; -using EntityDb.SqlDb.Queries.SortBuilders; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Documents.Command; - -internal sealed record CommandDocument : DocumentBase, IEntityDocument -{ - public static string TableName => "Commands"; - - private static readonly CommandFilterBuilder FilterBuilder = new(); - - private static readonly CommandSortBuilder SortBuilder = new(); - - private static readonly IDocumentReader EntityVersionNumberDocumentReader = new CommandEntityVersionNumberDocumentReader(); - - public static IDocumentReader DocumentReader { get; } = new CommandDocumentReader(); - - public static IDocumentReader TransactionIdDocumentReader { get; } = new CommandTransactionIdDocumentReader(); - - public static IDocumentReader DataDocumentReader { get; } = new CommandDataDocumentReader(); - - public static IDocumentReader EntityIdDocumentReader { get; } = new CommandEntityIdDocumentReader(); - - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } - - public Id GetSubjectId() => EntityId; - public VersionNumber GetSubjectVersionNumber() => EntityVersionNumber; - - public static InsertDocumentsCommand GetInsertCommand - ( - IEnvelopeService envelopeService, - ITransaction transaction, - ITransactionCommand transactionCommand - ) - { - return new InsertDocumentsCommand - ( - TableName, - new[] - { - new CommandDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.SubjectId, - EntityVersionNumber = transactionCommand.SubjectVersionNumber, - DataType = transactionCommand.Data.GetType().Name, - Data = envelopeService.Serialize(transactionCommand.Data) - } - } - ); - } - - public static DocumentQuery GetQuery - ( - ICommandQuery commandQuery - ) - { - return new DocumentQuery - ( - commandQuery.GetFilter(FilterBuilder), - commandQuery.GetSort(SortBuilder), - commandQuery.Skip, - commandQuery.Take, - commandQuery.Options - ); - } - - public static async Task GetLastEntityVersionNumber - ( - ISqlDbSession sqlDbSession, - Id entityId, - CancellationToken cancellationToken - ) - where TOptions : class - { - var commandQuery = new GetLastEntityCommandQuery(entityId); - - var documentQuery = GetQuery(commandQuery); - - var document = await documentQuery - .Execute(sqlDbSession, EntityVersionNumberDocumentReader, cancellationToken) - .SingleOrDefaultAsync(cancellationToken); - - return document?.EntityVersionNumber ?? default; - } - - public Dictionary ToDictionary() - { - return new Dictionary - { - [nameof(TransactionId)] = TransactionId, - [nameof(TransactionTimeStamp)] = TransactionTimeStamp, - [nameof(EntityId)] = EntityId, - [nameof(EntityVersionNumber)] = EntityVersionNumber, - [nameof(DataType)] = DataType, - [nameof(Data)] = Data, - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs deleted file mode 100644 index 9491d7a0..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Command; - -internal class CommandDocumentReader : CommandDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(CommandDocument.TransactionId), - nameof(CommandDocument.TransactionTimeStamp), - nameof(CommandDocument.EntityId), - nameof(CommandDocument.EntityVersionNumber), - nameof(CommandDocument.DataType), - nameof(CommandDocument.Data), - }; - - public CommandDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new CommandDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), - EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)), - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))), - DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs deleted file mode 100644 index 0c9eea15..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace EntityDb.SqlDb.Documents.Command; - -internal abstract class CommandDocumentReaderBase -{ - private readonly string[] _propertyNames; - - protected readonly int TransactionIdOrdinal; - protected readonly int TransactionTimeStampOrdinal; - protected readonly int EntityIdOrdinal; - protected readonly int EntityVersionNumberOrdinal; - protected readonly int DataTypeOrdinal; - protected readonly int DataOrdinal; - - public string[] GetPropertyNames() => _propertyNames; - - protected CommandDocumentReaderBase(string[] propertyNames) - { - _propertyNames = propertyNames; - - TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.TransactionId)); - TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.TransactionTimeStamp)); - EntityIdOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.EntityId)); - EntityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.EntityVersionNumber)); - DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.DataType)); - DataOrdinal = Array.IndexOf(_propertyNames, nameof(CommandDocument.Data)); - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs deleted file mode 100644 index 356b762d..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandEntityIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Command; - -internal class CommandEntityIdDocumentReader : CommandDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(CommandDocument.EntityId), - }; - - public CommandEntityIdDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new CommandDocument - { - EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs deleted file mode 100644 index c4cc3d4a..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Command; - -internal class CommandEntityVersionNumberDocumentReader : CommandDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(CommandDocument.EntityVersionNumber), - }; - - public CommandEntityVersionNumberDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new CommandDocument - { - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs deleted file mode 100644 index b5dbbb22..00000000 --- a/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Command; - -internal class CommandTransactionIdDocumentReader : CommandDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(CommandDocument.TransactionId), - }; - - public CommandTransactionIdDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new CommandDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/DocumentBase.cs b/src/EntityDb.SqlDb/Documents/DocumentBase.cs deleted file mode 100644 index f547ec9d..00000000 --- a/src/EntityDb.SqlDb/Documents/DocumentBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.SqlDb.Documents; - -internal abstract record DocumentBase -{ - public Guid? Id { get; init; } - public TimeStamp TransactionTimeStamp { get; init; } - public Id TransactionId { get; init; } - public string DataType { get; init; } = default!; - public string Data { get; init; } = default!; - - public Id GetSourceId() => TransactionId; - public TimeStamp GetSourceTimeStamp() => TransactionTimeStamp; -} diff --git a/src/EntityDb.SqlDb/Documents/IDocumentReader.cs b/src/EntityDb.SqlDb/Documents/IDocumentReader.cs deleted file mode 100644 index 73045465..00000000 --- a/src/EntityDb.SqlDb/Documents/IDocumentReader.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents; - -internal interface IDocumentReader -{ - string[] GetPropertyNames(); -} - -internal interface IDocumentReader : IDocumentReader -{ - Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken); -} diff --git a/src/EntityDb.SqlDb/Documents/IEntitiesDocument.cs b/src/EntityDb.SqlDb/Documents/IEntitiesDocument.cs deleted file mode 100644 index f918b821..00000000 --- a/src/EntityDb.SqlDb/Documents/IEntitiesDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace EntityDb.SqlDb.Documents; - -internal interface IEntitiesDocument : Common.Documents.IEntitiesDocument, ITransactionDocument -{ -} - -internal interface IEntitiesDocument : IEntitiesDocument, ITransactionDocument - where TDocument : IEntitiesDocument -{ - static abstract IDocumentReader EntityIdsDocumentReader { get; } -} diff --git a/src/EntityDb.SqlDb/Documents/IEntityDocument.cs b/src/EntityDb.SqlDb/Documents/IEntityDocument.cs deleted file mode 100644 index 819edd2a..00000000 --- a/src/EntityDb.SqlDb/Documents/IEntityDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace EntityDb.SqlDb.Documents; - -internal interface IEntityDocument : Common.Documents.IEntityDocument, ITransactionDocument -{ -} - -internal interface IEntityDocument : IEntityDocument, ITransactionDocument - where TDocument : IEntityDocument -{ - static abstract IDocumentReader EntityIdDocumentReader { get; } -} diff --git a/src/EntityDb.SqlDb/Documents/ITransactionDocument.cs b/src/EntityDb.SqlDb/Documents/ITransactionDocument.cs deleted file mode 100644 index 99ddc5e7..00000000 --- a/src/EntityDb.SqlDb/Documents/ITransactionDocument.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace EntityDb.SqlDb.Documents; - -internal interface ITransactionDocument : Common.Documents.ITransactionDocument -{ - static abstract string TableName { get; } - - Guid? Id { get; } - - Dictionary ToDictionary(); -} - -internal interface ITransactionDocument : ITransactionDocument - where TDocument : ITransactionDocument -{ - static abstract IDocumentReader DocumentReader { get; } - static abstract IDocumentReader TransactionIdDocumentReader { get; } - static abstract IDocumentReader DataDocumentReader { get; } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs deleted file mode 100644 index f6e8d108..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDataDocumentReader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Lease; - -internal class LeaseDataDocumentReader : LeaseDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(LeaseDocument.Data), - }; - - public LeaseDataDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new LeaseDocument - { - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs deleted file mode 100644 index b16d0250..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ /dev/null @@ -1,111 +0,0 @@ -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Queries; -using EntityDb.SqlDb.Commands; -using EntityDb.SqlDb.Queries; -using EntityDb.SqlDb.Queries.FilterBuilders; -using EntityDb.SqlDb.Queries.SortBuilders; - -namespace EntityDb.SqlDb.Documents.Lease; - -internal sealed record LeaseDocument : DocumentBase, IEntityDocument -{ - public static string TableName => "Leases"; - - private static readonly LeaseFilterBuilder FilterBuilder = new(); - - private static readonly LeaseSortBuilder SortBuilder = new(); - - public string Scope { get; init; } = default!; - public string Label { get; init; } = default!; - public string Value { get; init; } = default!; - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } - - public Id GetSubjectId() => EntityId; - public VersionNumber GetSubjectVersionNumber() => EntityVersionNumber; - - public static IDocumentReader DocumentReader { get; } = new LeaseDocumentReader(); - - public static IDocumentReader TransactionIdDocumentReader { get; } = new LeaseTransactionIdDocumentReader(); - - public static IDocumentReader DataDocumentReader { get; } = new LeaseDataDocumentReader(); - - public static IDocumentReader EntityIdDocumentReader { get; } = new LeaseEntityIdDocumentReader(); - - public static InsertDocumentsCommand GetInsertCommand - ( - IEnvelopeService envelopeService, - ITransaction transaction, - ITransactionCommand transactionCommand - ) - { - return new InsertDocumentsCommand - ( - TableName, - transactionCommand.AddLeases - .Select(lease => new LeaseDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.SubjectId, - EntityVersionNumber = transactionCommand.SubjectVersionNumber, - Scope = lease.Scope, - Label = lease.Label, - Value = lease.Value, - DataType = lease.GetType().Name, - Data = envelopeService.Serialize(lease) - }) - .ToArray() - ); - } - - public static DocumentQuery GetQuery - ( - ILeaseQuery leaseQuery - ) - { - return new DocumentQuery - ( - leaseQuery.GetFilter(FilterBuilder), - leaseQuery.GetSort(SortBuilder), - leaseQuery.Skip, - leaseQuery.Take, - leaseQuery.Options - ); - } - - public static DeleteDocumentsCommand GetDeleteCommand - ( - ITransactionCommand transactionCommand - ) - { - var deleteLeasesQuery = - new DeleteLeasesQuery(transactionCommand.SubjectId, transactionCommand.DeleteLeases); - - return new DeleteDocumentsCommand - ( - TableName, - deleteLeasesQuery.GetFilter(FilterBuilder) - ); - } - - public Dictionary ToDictionary() - { - return new Dictionary - { - [nameof(TransactionId)] = TransactionId, - [nameof(TransactionTimeStamp)] = TransactionTimeStamp, - [nameof(EntityId)] = EntityId, - [nameof(EntityVersionNumber)] = EntityVersionNumber, - [nameof(DataType)] = DataType, - [nameof(Data)] = Data, - [nameof(Scope)] = Scope, - [nameof(Label)] = Label, - [nameof(Value)] = Value, - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs deleted file mode 100644 index e346949f..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReader.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Lease; - -internal class LeaseDocumentReader : LeaseDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(LeaseDocument.TransactionId), - nameof(LeaseDocument.TransactionTimeStamp), - nameof(LeaseDocument.EntityId), - nameof(LeaseDocument.EntityVersionNumber), - nameof(LeaseDocument.DataType), - nameof(LeaseDocument.Data), - nameof(LeaseDocument.Scope), - nameof(LeaseDocument.Label), - nameof(LeaseDocument.Value), - }; - - public LeaseDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new LeaseDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), - EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)), - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))), - DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), - Scope = await dbDataReader.GetFieldValueAsync(ScopeOrdinal, cancellationToken), - Label = await dbDataReader.GetFieldValueAsync(LabelOrdinal, cancellationToken), - Value = await dbDataReader.GetFieldValueAsync(ValueOrdinal, cancellationToken), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs deleted file mode 100644 index 9c300b04..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace EntityDb.SqlDb.Documents.Lease; - -internal abstract class LeaseDocumentReaderBase -{ - private readonly string[] _propertyNames; - - protected readonly int TransactionIdOrdinal; - protected readonly int TransactionTimeStampOrdinal; - protected readonly int EntityIdOrdinal; - protected readonly int EntityVersionNumberOrdinal; - protected readonly int DataTypeOrdinal; - protected readonly int DataOrdinal; - protected readonly int ScopeOrdinal; - protected readonly int LabelOrdinal; - protected readonly int ValueOrdinal; - - public string[] GetPropertyNames() => _propertyNames; - - protected LeaseDocumentReaderBase(string[] propertyNames) - { - _propertyNames = propertyNames; - - TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.TransactionId)); - TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.TransactionTimeStamp)); - EntityIdOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.EntityId)); - EntityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.EntityVersionNumber)); - DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.DataType)); - DataOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Data)); - ScopeOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Scope)); - LabelOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Label)); - ValueOrdinal = Array.IndexOf(_propertyNames, nameof(LeaseDocument.Value)); - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs deleted file mode 100644 index f48129a7..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseEntityIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Lease; - -internal class LeaseEntityIdDocumentReader : LeaseDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyName = - { - nameof(LeaseDocument.EntityId), - }; - - public LeaseEntityIdDocumentReader() : base(PropertyName) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new LeaseDocument - { - EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs deleted file mode 100644 index bc5b8c54..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Lease; - -internal class LeaseTransactionIdDocumentReader : LeaseDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(LeaseDocument.TransactionId), - }; - - public LeaseTransactionIdDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new LeaseDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs deleted file mode 100644 index 6b4ee487..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Tag; - -internal class TagDataDocumentReader : TagDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(TagDocument.Data), - }; - - public TagDataDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new TagDocument - { - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs deleted file mode 100644 index 10e480a3..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ /dev/null @@ -1,107 +0,0 @@ -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Queries; -using EntityDb.SqlDb.Commands; -using EntityDb.SqlDb.Queries; -using EntityDb.SqlDb.Queries.FilterBuilders; -using EntityDb.SqlDb.Queries.SortBuilders; - -namespace EntityDb.SqlDb.Documents.Tag; - -internal sealed record TagDocument : DocumentBase, IEntityDocument -{ - public static string TableName => "Tags"; - - private static readonly TagFilterBuilder FilterBuilder = new(); - - private static readonly TagSortBuilder SortBuilder = new(); - - public string Label { get; init; } = default!; - public string Value { get; init; } = default!; - public Id EntityId { get; init; } - public VersionNumber EntityVersionNumber { get; init; } - - public Id GetSubjectId() => EntityId; - public VersionNumber GetSubjectVersionNumber() => EntityVersionNumber; - - public static IDocumentReader DocumentReader { get; } = new TagDocumentReader(); - - public static IDocumentReader TransactionIdDocumentReader { get; } = new TagTransactionIdDocumentReader(); - - public static IDocumentReader DataDocumentReader { get; } = new TagDataDocumentReader(); - - public static IDocumentReader EntityIdDocumentReader { get; } = new TagEntityIdDocumentReader(); - - public static InsertDocumentsCommand GetInsertCommand - ( - IEnvelopeService envelopeService, - ITransaction transaction, - ITransactionCommand transactionCommand - ) - { - return new InsertDocumentsCommand - ( - TableName, - transactionCommand.AddTags - .Select(tag => new TagDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = transactionCommand.SubjectId, - EntityVersionNumber = transactionCommand.SubjectVersionNumber, - Label = tag.Label, - Value = tag.Value, - DataType = tag.GetType().Name, - Data = envelopeService.Serialize(tag) - }) - .ToArray() - ); - } - - public static DocumentQuery GetQuery - ( - ITagQuery tagQuery - ) - { - return new DocumentQuery - ( - tagQuery.GetFilter(FilterBuilder), - tagQuery.GetSort(SortBuilder), - tagQuery.Skip, - tagQuery.Take, - tagQuery.Options - ); - } - - public static DeleteDocumentsCommand GetDeleteCommand - ( - ITransactionCommand transactionCommand - ) - { - var deleteTagsQuery = new DeleteTagsQuery(transactionCommand.SubjectId, transactionCommand.DeleteTags); - - return new DeleteDocumentsCommand - ( - TableName, - deleteTagsQuery.GetFilter(FilterBuilder) - ); - } - - public Dictionary ToDictionary() - { - return new Dictionary - { - [nameof(TransactionId)] = TransactionId, - [nameof(TransactionTimeStamp)] = TransactionTimeStamp, - [nameof(EntityId)] = EntityId, - [nameof(EntityVersionNumber)] = EntityVersionNumber, - [nameof(DataType)] = DataType, - [nameof(Data)] = Data, - [nameof(Label)] = Label, - [nameof(Value)] = Value, - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs deleted file mode 100644 index bda2c50e..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReader.cs +++ /dev/null @@ -1,38 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Tag; - -internal class TagDocumentReader : TagDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(TagDocument.TransactionId), - nameof(TagDocument.TransactionTimeStamp), - nameof(TagDocument.EntityId), - nameof(TagDocument.EntityVersionNumber), - nameof(TagDocument.DataType), - nameof(TagDocument.Data), - nameof(TagDocument.Label), - nameof(TagDocument.Value), - }; - - public TagDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new TagDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(TransactionTimeStampOrdinal, cancellationToken)), - EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)), - EntityVersionNumber = new VersionNumber(Convert.ToUInt64(await dbDataReader.GetFieldValueAsync(EntityVersionNumberOrdinal, cancellationToken))), - DataType = await dbDataReader.GetFieldValueAsync(DataTypeOrdinal, cancellationToken), - Data = await dbDataReader.GetFieldValueAsync(DataOrdinal, cancellationToken), - Label = await dbDataReader.GetFieldValueAsync(LabelOrdinal, cancellationToken), - Value = await dbDataReader.GetFieldValueAsync(ValueOrdinal, cancellationToken), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs deleted file mode 100644 index 1ab4be38..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace EntityDb.SqlDb.Documents.Tag; - -internal abstract class TagDocumentReaderBase -{ - private readonly string[] _propertyNames; - - protected readonly int TransactionIdOrdinal; - protected readonly int TransactionTimeStampOrdinal; - protected readonly int EntityIdOrdinal; - protected readonly int EntityVersionNumberOrdinal; - protected readonly int DataTypeOrdinal; - protected readonly int DataOrdinal; - protected readonly int LabelOrdinal; - protected readonly int ValueOrdinal; - - public string[] GetPropertyNames() => _propertyNames; - - protected TagDocumentReaderBase(string[] propertyNames) - { - _propertyNames = propertyNames; - - TransactionIdOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.TransactionId)); - TransactionTimeStampOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.TransactionTimeStamp)); - EntityIdOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.EntityId)); - EntityVersionNumberOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.EntityVersionNumber)); - DataTypeOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.DataType)); - DataOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Data)); - LabelOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Label)); - ValueOrdinal = Array.IndexOf(_propertyNames, nameof(TagDocument.Value)); - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs deleted file mode 100644 index e6c8f883..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagEntityIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Tag; - -internal class TagEntityIdDocumentReader : TagDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(TagDocument.EntityId), - }; - - public TagEntityIdDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new TagDocument - { - EntityId = new Id(await dbDataReader.GetFieldValueAsync(EntityIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs deleted file mode 100644 index 75ad2cf2..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; -using System.Data.Common; - -namespace EntityDb.SqlDb.Documents.Tag; - -internal class TagTransactionIdDocumentReader : TagDocumentReaderBase, IDocumentReader -{ - private static readonly string[] PropertyNames = - { - nameof(TagDocument.TransactionId), - }; - - public TagTransactionIdDocumentReader() : base(PropertyNames) - { - } - - public async Task Read(DbDataReader dbDataReader, CancellationToken cancellationToken) - { - return new TagDocument - { - TransactionId = new Id(await dbDataReader.GetFieldValueAsync(TransactionIdOrdinal, cancellationToken)) - }; - } -} diff --git a/src/EntityDb.SqlDb/EntityDb.SqlDb.csproj b/src/EntityDb.SqlDb/EntityDb.SqlDb.csproj deleted file mode 100644 index 7760c104..00000000 --- a/src/EntityDb.SqlDb/EntityDb.SqlDb.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs b/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs deleted file mode 100644 index d9b1b218..00000000 --- a/src/EntityDb.SqlDb/Extensions/DocumentQueryExtensions.cs +++ /dev/null @@ -1,155 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Extensions; -using EntityDb.Common.Polyfills; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries; -using EntityDb.SqlDb.Sessions; -using System.Runtime.CompilerServices; - -namespace EntityDb.SqlDb.Extensions; - -internal static class DocumentQueryExtensions -{ - - private static IAsyncEnumerable EnumerateIds - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - IDocumentReader documentReader, - Func, IAsyncEnumerable> mapToIds, - CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - where TOptions : class - { - var skip = documentQuery.Skip; - var limit = documentQuery.Limit; - - documentQuery = documentQuery with { Skip = null, Limit = null }; - - var documents = documentQuery.Execute(sqlDbSession, documentReader, cancellationToken); - - return documents.EnumerateIds(skip, limit, mapToIds); - } - - public static IAsyncEnumerable EnumerateTransactionIds - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - CancellationToken cancellationToken - ) - where TOptions : class - where TDocument : ITransactionDocument - { - return documentQuery.EnumerateIds - ( - sqlDbSession, - TDocument.TransactionIdDocumentReader, - documents => documents.Select(document => document.GetSourceId()), - cancellationToken - ); - } - - public static IAsyncEnumerable EnumerateEntityIds - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - CancellationToken cancellationToken - ) - where TOptions : class - where TDocument : IEntityDocument - { - return documentQuery.EnumerateIds - ( - sqlDbSession, - TDocument.EntityIdDocumentReader, - documents => documents.Select(document => document.GetSubjectId()), - cancellationToken - ); - } - - public static IAsyncEnumerable EnumerateEntitiesIds - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - CancellationToken cancellationToken - ) - where TOptions : class - where TDocument : IEntitiesDocument - { - return documentQuery.EnumerateIds - ( - sqlDbSession, - TDocument.EntityIdsDocumentReader, - documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.GetSubjectIds())), - cancellationToken - ); - } - - public static async IAsyncEnumerable EnumerateData - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - IEnvelopeService envelopeService, - [EnumeratorCancellation] CancellationToken cancellationToken - ) - where TOptions : class - where TDocument : ITransactionDocument - { - var documents = documentQuery.Execute - ( - sqlDbSession, - TDocument.DataDocumentReader, - cancellationToken - ); - - await foreach (var document in documents) - { - yield return envelopeService.Deserialize(document.Data); - } - } - - public static IAsyncEnumerable> EnumerateEntityAnnotation - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - IEnvelopeService envelopeService, - CancellationToken cancellationToken - ) - where TOptions : class - where TDocument : IEntityDocument - where TData : notnull - { - var documents = documentQuery.Execute - ( - sqlDbSession, - TDocument.DocumentReader, - cancellationToken - ); - - return documents.EnumerateEntityAnnotation(envelopeService, cancellationToken); - } - - public static IAsyncEnumerable> EnumerateEntitiesAnnotation - ( - this DocumentQuery documentQuery, - ISqlDbSession sqlDbSession, - IEnvelopeService envelopeService, - CancellationToken cancellationToken - ) - where TOptions : class - where TDocument : IEntitiesDocument - where TData : notnull - { - var documents = documentQuery.Execute - ( - sqlDbSession, - TDocument.DocumentReader, - cancellationToken - ); - - return documents.EnumerateEntitiesAnnotation(envelopeService, cancellationToken); - } -} diff --git a/src/EntityDb.SqlDb/Extensions/SqlDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.SqlDb/Extensions/SqlDbTransactionRepositoryFactoryExtensions.cs deleted file mode 100644 index 252d1c7a..00000000 --- a/src/EntityDb.SqlDb/Extensions/SqlDbTransactionRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.SqlDb.Transactions; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.SqlDb.Extensions; - -internal static class SqlDbTransactionRepositoryFactoryExtensions -{ - [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] - public static ISqlDbTransactionRepositoryFactory UseTestMode( - this ISqlDbTransactionRepositoryFactory mongoDbTransactionRepositoryFactory, - bool testMode) - where TOptions : class - { - return testMode - ? new TestModeSqlDbTransactionRepositoryFactory(mongoDbTransactionRepositoryFactory) - : mongoDbTransactionRepositoryFactory; - } -} diff --git a/src/EntityDb.SqlDb/Properties/AssemblyInfo.cs b/src/EntityDb.SqlDb/Properties/AssemblyInfo.cs deleted file mode 100644 index 56bef749..00000000 --- a/src/EntityDb.SqlDb/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Runtime.CompilerServices; - -// src -[assembly: InternalsVisibleTo("EntityDb.Npgsql")] -[assembly: InternalsVisibleTo("EntityDb.Provisioner")] - -// test diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/AndFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/AndFilterDefinition.cs deleted file mode 100644 index e22d19b5..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/AndFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct AndFilterDefinition(IFilterDefinition[] FilterDefinitions) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/AnyInFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/AnyInFilterDefinition.cs deleted file mode 100644 index 36469558..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/AnyInFilterDefinition.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Collections; - -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct AnyInFilterDefinition(string PropertyName, IEnumerable PropertyValues) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/EqFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/EqFilterDefinition.cs deleted file mode 100644 index 60d887e5..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/EqFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct EqFilterDefinition(string PropertyName, object PropertyValue) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/GteFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/GteFilterDefinition.cs deleted file mode 100644 index 9b196c86..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/GteFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct GteFilterDefinition(string PropertyName, object PropertyValue) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/IFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/IFilterDefinition.cs deleted file mode 100644 index ac5a461c..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/IFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal interface IFilterDefinition { } diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/InFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/InFilterDefinition.cs deleted file mode 100644 index 223da436..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/InFilterDefinition.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Collections; - -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct InFilterDefinition(string PropertyName, IEnumerable PropertyValues) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/LteFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/LteFilterDefinition.cs deleted file mode 100644 index 8928b810..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/LteFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct LteFilterDefinition(string PropertyName, object PropertyValue) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/NotFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/NotFilterDefinition.cs deleted file mode 100644 index 8510ce28..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/NotFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct NotFilterDefinition(IFilterDefinition FilterDefinition) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Filter/OrFilterDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Filter/OrFilterDefinition.cs deleted file mode 100644 index 99fb75ae..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Filter/OrFilterDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Filter; - -internal record struct OrFilterDefinition(IFilterDefinition[] FilterDefinitions) : IFilterDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Sort/AscSortDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Sort/AscSortDefinition.cs deleted file mode 100644 index 9a44c317..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Sort/AscSortDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Sort; - -internal record struct AscSortDefinition(string PropertyName) : ISortDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Sort/CombineSortDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Sort/CombineSortDefinition.cs deleted file mode 100644 index d98b606b..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Sort/CombineSortDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Sort; - -internal record struct CombineSortDefinition(ISortDefinition[] SortDefinitions) : ISortDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Sort/DescSortDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Sort/DescSortDefinition.cs deleted file mode 100644 index cb92a9bc..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Sort/DescSortDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Sort; - -internal record struct DescSortDefinition(string PropertyName) : ISortDefinition; diff --git a/src/EntityDb.SqlDb/Queries/Definitions/Sort/ISortDefinition.cs b/src/EntityDb.SqlDb/Queries/Definitions/Sort/ISortDefinition.cs deleted file mode 100644 index 974f4d17..00000000 --- a/src/EntityDb.SqlDb/Queries/Definitions/Sort/ISortDefinition.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EntityDb.SqlDb.Queries.Definitions.Sort; - -internal interface ISortDefinition { } diff --git a/src/EntityDb.SqlDb/Queries/DocumentQuery.cs b/src/EntityDb.SqlDb/Queries/DocumentQuery.cs deleted file mode 100644 index 156f60d3..00000000 --- a/src/EntityDb.SqlDb/Queries/DocumentQuery.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Queries.Definitions.Sort; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Queries; - -internal record DocumentQuery -( - IFilterDefinition FilterDefinition, - ISortDefinition? SortDefinition, - int? Skip, - int? Limit, - object? Options -) - where TDocument : ITransactionDocument -{ - public IAsyncEnumerable Execute(ISqlDbSession sqlDbSession, IDocumentReader documentReader, CancellationToken cancellationToken) - where TOptions : class - { - return sqlDbSession.Find(documentReader, FilterDefinition, SortDefinition, Skip, Limit, Options as TOptions, cancellationToken); - } -} diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs deleted file mode 100644 index 17c0f8b7..00000000 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.SqlDb.Documents.AgentSignature; -using EntityDb.SqlDb.Queries.Definitions.Filter; - -namespace EntityDb.SqlDb.Queries.FilterBuilders; - -internal sealed class AgentSignatureFilterBuilder : FilterBuilderBase, IAgentSignatureFilterBuilder -{ - public IFilterDefinition SubjectIdsIn(params Id[] subjectIds) - { - return AnyIn(nameof(AgentSignatureDocument.EntityIds), subjectIds); - } - - public IFilterDefinition SubjectPointersIn(params Pointer[] subjectPointers) - { - return AnyIn(nameof(AgentSignatureDocument.EntityPointers), subjectPointers); - } - - public IFilterDefinition AgentSignatureTypeIn(params Type[] agentSignatureTypes) - { - return DataTypeIn(agentSignatureTypes); - } -} diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/CommandFilterBuilder.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/CommandFilterBuilder.cs deleted file mode 100644 index edfb8c22..00000000 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/CommandFilterBuilder.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.SqlDb.Documents.Command; -using EntityDb.SqlDb.Queries.Definitions.Filter; - -namespace EntityDb.SqlDb.Queries.FilterBuilders; - -internal sealed class CommandFilterBuilder : FilterBuilderBase, ICommandFilterBuilder -{ - public IFilterDefinition EntityIdIn(params Id[] entityIds) - { - return In(nameof(CommandDocument.EntityId), entityIds); - } - - public IFilterDefinition EntityVersionNumberGte(VersionNumber entityVersionNumber) - { - return Gte(nameof(CommandDocument.EntityVersionNumber), entityVersionNumber); - } - - public IFilterDefinition EntityVersionNumberLte(VersionNumber entityVersionNumber) - { - return Lte(nameof(CommandDocument.EntityVersionNumber), entityVersionNumber); - } - - public IFilterDefinition CommandTypeIn(params Type[] commandTypes) - { - return DataTypeIn(commandTypes); - } -} diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs deleted file mode 100644 index 7e59cd24..00000000 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/FilterBuilderBase.cs +++ /dev/null @@ -1,77 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Envelopes; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Filter; - -namespace EntityDb.SqlDb.Queries.FilterBuilders; - -internal abstract class FilterBuilderBase : IFilterBuilder -{ - public IFilterDefinition SourceTimeStampGte(TimeStamp timeStamp) - { - return Gte(nameof(ITransactionDocument.TransactionTimeStamp), timeStamp); - } - - public IFilterDefinition SourceTimeStampLte(TimeStamp timeStamp) - { - return Lte(nameof(ITransactionDocument.TransactionTimeStamp), timeStamp); - } - - public IFilterDefinition SourceIdIn(params Id[] transactionIds) - { - return In(nameof(ITransactionDocument.TransactionId), transactionIds); - } - - protected static IFilterDefinition DataTypeIn(params Type[] dataTypes) - { - var typeNames = dataTypes.GetTypeHeaderValues(); - - return In(nameof(ITransactionDocument.DataType), typeNames); - } - - public IFilterDefinition Not(IFilterDefinition filter) - { - return new NotFilterDefinition(filter); - } - - public IFilterDefinition And(params IFilterDefinition[] filters) - { - return new AndFilterDefinition(filters); - } - - public IFilterDefinition Or(params IFilterDefinition[] filters) - { - return new OrFilterDefinition(filters); - } - - protected static IFilterDefinition Eq(string fieldName, TValue value) - where TValue : notnull - { - return new EqFilterDefinition(fieldName, value); - } - - protected static IFilterDefinition In(string fieldName, IEnumerable values) - where TValue : notnull - { - return new InFilterDefinition(fieldName, values); - } - - protected static IFilterDefinition AnyIn(string fieldName, IEnumerable values) - where TValue : notnull - { - return new AnyInFilterDefinition(fieldName, values); - } - - protected static IFilterDefinition Gte(string fieldName, TValue value) - where TValue : notnull - { - return new GteFilterDefinition(fieldName, value); - } - - protected static IFilterDefinition Lte(string fieldName, TValue value) - where TValue : notnull - { - return new LteFilterDefinition(fieldName, value); - } -} diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/LeaseFilterBuilder.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/LeaseFilterBuilder.cs deleted file mode 100644 index d078b166..00000000 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/LeaseFilterBuilder.cs +++ /dev/null @@ -1,44 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.SqlDb.Documents.Lease; -using EntityDb.SqlDb.Queries.Definitions.Filter; - -namespace EntityDb.SqlDb.Queries.FilterBuilders; - -internal sealed class LeaseFilterBuilder : FilterBuilderBase, ILeaseFilterBuilder -{ - public IFilterDefinition EntityIdIn(params Id[] entityIds) - { - return In(nameof(LeaseDocument.EntityId), entityIds); - } - - public IFilterDefinition EntityVersionNumberGte(VersionNumber entityVersionNumber) - { - return Gte(nameof(LeaseDocument.EntityVersionNumber), entityVersionNumber); - } - - public IFilterDefinition EntityVersionNumberLte(VersionNumber entityVersionNumber) - { - return Lte(nameof(LeaseDocument.EntityVersionNumber), entityVersionNumber); - } - - public IFilterDefinition LeaseTypeIn(params Type[] leaseTypes) - { - return DataTypeIn(leaseTypes); - } - - public IFilterDefinition LeaseScopeEq(string scope) - { - return Eq(nameof(LeaseDocument.Scope), scope); - } - - public IFilterDefinition LeaseLabelEq(string label) - { - return Eq(nameof(LeaseDocument.Label), label); - } - - public IFilterDefinition LeaseValueEq(string value) - { - return Eq(nameof(LeaseDocument.Value), value); - } -} diff --git a/src/EntityDb.SqlDb/Queries/FilterBuilders/TagFilterBuilder.cs b/src/EntityDb.SqlDb/Queries/FilterBuilders/TagFilterBuilder.cs deleted file mode 100644 index 043aa9a7..00000000 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/TagFilterBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.SqlDb.Documents.Tag; -using EntityDb.SqlDb.Queries.Definitions.Filter; - -namespace EntityDb.SqlDb.Queries.FilterBuilders; - -internal sealed class TagFilterBuilder : FilterBuilderBase, ITagFilterBuilder -{ - public IFilterDefinition EntityIdIn(params Id[] entityIds) - { - return In(nameof(TagDocument.EntityId), entityIds); - } - - public IFilterDefinition EntityVersionNumberGte(VersionNumber entityVersionNumber) - { - return Gte(nameof(TagDocument.EntityVersionNumber), entityVersionNumber); - } - - public IFilterDefinition EntityVersionNumberLte(VersionNumber entityVersionNumber) - { - return Lte(nameof(TagDocument.EntityVersionNumber), entityVersionNumber); - } - - public IFilterDefinition TagTypeIn(params Type[] tagTypes) - { - return DataTypeIn(tagTypes); - } - - public IFilterDefinition TagLabelEq(string label) - { - return Eq(nameof(TagDocument.Label), label); - } - - public IFilterDefinition TagValueEq(string value) - { - return Eq(nameof(TagDocument.Value), value); - } -} diff --git a/src/EntityDb.SqlDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs b/src/EntityDb.SqlDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs deleted file mode 100644 index 561b170e..00000000 --- a/src/EntityDb.SqlDb/Queries/SortBuilders/AgentSignatureSortBuilder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.SqlDb.Documents.AgentSignature; -using EntityDb.SqlDb.Queries.Definitions.Sort; - -namespace EntityDb.SqlDb.Queries.SortBuilders; - -internal sealed class AgentSignatureSortBuilder : SortBuilderBase, IAgentSignatureSortBuilder -{ - public ISortDefinition EntityIds(bool ascending) - { - return Sort(ascending, nameof(AgentSignatureDocument.EntityIds)); - } - - public ISortDefinition AgentSignatureType(bool ascending) - { - return Sort(ascending, nameof(AgentSignatureDocument.DataType)); - } -} diff --git a/src/EntityDb.SqlDb/Queries/SortBuilders/CommandSortBuilder.cs b/src/EntityDb.SqlDb/Queries/SortBuilders/CommandSortBuilder.cs deleted file mode 100644 index 762b810c..00000000 --- a/src/EntityDb.SqlDb/Queries/SortBuilders/CommandSortBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.SqlDb.Documents.Command; -using EntityDb.SqlDb.Queries.Definitions.Sort; - -namespace EntityDb.SqlDb.Queries.SortBuilders; - -internal sealed class CommandSortBuilder : SortBuilderBase, ICommandSortBuilder -{ - public ISortDefinition EntityId(bool ascending) - { - return Sort(ascending, nameof(CommandDocument.EntityId)); - } - - public ISortDefinition EntityVersionNumber(bool ascending) - { - return Sort(ascending, nameof(CommandDocument.EntityVersionNumber)); - } - - public ISortDefinition CommandType(bool ascending) - { - return SortDataType(ascending); - } -} diff --git a/src/EntityDb.SqlDb/Queries/SortBuilders/LeaseSortBuilder.cs b/src/EntityDb.SqlDb/Queries/SortBuilders/LeaseSortBuilder.cs deleted file mode 100644 index 853ea076..00000000 --- a/src/EntityDb.SqlDb/Queries/SortBuilders/LeaseSortBuilder.cs +++ /dev/null @@ -1,38 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.SqlDb.Documents.Lease; -using EntityDb.SqlDb.Queries.Definitions.Sort; - -namespace EntityDb.SqlDb.Queries.SortBuilders; - -internal sealed class LeaseSortBuilder : SortBuilderBase, ILeaseSortBuilder -{ - public ISortDefinition EntityId(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.EntityId)); - } - - public ISortDefinition EntityVersionNumber(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.EntityVersionNumber)); - } - - public ISortDefinition LeaseType(bool ascending) - { - return SortDataType(ascending); - } - - public ISortDefinition LeaseScope(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.Scope)); - } - - public ISortDefinition LeaseLabel(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.Label)); - } - - public ISortDefinition LeaseValue(bool ascending) - { - return Sort(ascending, nameof(LeaseDocument.Value)); - } -} diff --git a/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs b/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs deleted file mode 100644 index 3220dbe4..00000000 --- a/src/EntityDb.SqlDb/Queries/SortBuilders/SortBuilderBase.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Sort; - -namespace EntityDb.SqlDb.Queries.SortBuilders; - -internal abstract class SortBuilderBase : ISortBuilder -{ - public ISortDefinition SourceTimeStamp(bool ascending) - { - return Sort(ascending, nameof(ITransactionDocument.TransactionTimeStamp)); - } - - public ISortDefinition SourceId(bool ascending) - { - return Sort(ascending, nameof(ITransactionDocument.TransactionId)); - } - - - public ISortDefinition Combine(params ISortDefinition[] sorts) - { - return new CombineSortDefinition(sorts); - } - - protected static ISortDefinition SortDataType(bool ascending) - { - return Sort(ascending, nameof(ITransactionDocument.DataType)); - } - - protected static ISortDefinition Sort(bool ascending, string fieldName) - { - return ascending - ? new AscSortDefinition(fieldName) - : new DescSortDefinition(fieldName); - } -} diff --git a/src/EntityDb.SqlDb/Queries/SortBuilders/TagSortBuilder.cs b/src/EntityDb.SqlDb/Queries/SortBuilders/TagSortBuilder.cs deleted file mode 100644 index 55f8d1f8..00000000 --- a/src/EntityDb.SqlDb/Queries/SortBuilders/TagSortBuilder.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.SqlDb.Documents.Tag; -using EntityDb.SqlDb.Queries.Definitions.Sort; - -namespace EntityDb.SqlDb.Queries.SortBuilders; - -internal sealed class TagSortBuilder : SortBuilderBase, ITagSortBuilder -{ - public ISortDefinition EntityId(bool ascending) - { - return Sort(ascending, nameof(TagDocument.EntityId)); - } - - public ISortDefinition EntityVersionNumber(bool ascending) - { - return Sort(ascending, nameof(TagDocument.EntityVersionNumber)); - } - - public ISortDefinition TagType(bool ascending) - { - return SortDataType(ascending); - } - - public ISortDefinition TagLabel(bool ascending) - { - return Sort(ascending, nameof(TagDocument.Label)); - } - - public ISortDefinition TagValue(bool ascending) - { - return Sort(ascending, nameof(TagDocument.Value)); - } -} diff --git a/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs b/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs deleted file mode 100644 index 37b79166..00000000 --- a/src/EntityDb.SqlDb/Sessions/ISqlDbSession.cs +++ /dev/null @@ -1,46 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Queries.Definitions.Sort; -using System.Data.Common; - -namespace EntityDb.SqlDb.Sessions; - -internal interface ISqlDbSession : IDisposableResource - where TOptions : class -{ - DbConnection DbConnection { get; } - - Task Insert - ( - - string tableName, - TDocument[] documents, - CancellationToken cancellationToken - ) where TDocument : ITransactionDocument; - - IAsyncEnumerable Find - ( - IDocumentReader documentReader, - IFilterDefinition filterDefinition, - ISortDefinition? sortDefinition, - int? skip, - int? limit, - TOptions? options, - CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument; - - Task Delete - ( - string tableName, - IFilterDefinition filterDefinition, - CancellationToken cancellationToken - ); - - Task StartTransaction(CancellationToken cancellationToken = default); - Task CommitTransaction(CancellationToken cancellationToken = default); - Task AbortTransaction(CancellationToken cancellationToken = default); - - ISqlDbSession WithTransactionSessionOptions(SqlDbTransactionSessionOptions transactionSessionOptions); -} diff --git a/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs b/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs deleted file mode 100644 index 7dfe1e24..00000000 --- a/src/EntityDb.SqlDb/Sessions/SqlDbSession.cs +++ /dev/null @@ -1,229 +0,0 @@ -using EntityDb.Common.Disposables; -using EntityDb.Common.Exceptions; -using EntityDb.SqlDb.Converters; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Queries.Definitions.Sort; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System.Data.Common; -using System.Runtime.CompilerServices; - -namespace EntityDb.SqlDb.Sessions; - -internal class SqlDbSession : DisposableResourceBaseClass, - ISqlDbSession - where TOptions : class -{ - private readonly ILogger> _logger; - private readonly ISqlConverter _sqlConverter; - private readonly SqlDbTransactionSessionOptions _options; - - private DbTransaction? _dbTransaction; - - public DbConnection DbConnection { get; } - - public SqlDbSession - ( - ILogger> logger, - ISqlConverter sqlConverter, - SqlDbTransactionSessionOptions options, - DbConnection dbConnection - ) - { - _logger = logger; - _sqlConverter = sqlConverter; - _options = options; - - DbConnection = dbConnection; - } - - public async Task Insert - ( - string tableName, - TDocument[] documents, - CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - { - AssertNotReadOnly(); - - var dbCommand = _sqlConverter.ConvertInsert(tableName, documents); - - dbCommand.Connection = DbConnection; - dbCommand.Transaction = _dbTransaction; - - _logger - .LogInformation - ( - "Started Running {SqlType} Insert on `{Database}.{TableName}`\n\nCommand: {Command}\n\nDocuments Inserted: {DocumentsInserted}", - _sqlConverter.SqlType, - DbConnection.Database, - tableName, - dbCommand.CommandText, - documents.Length - ); - - await dbCommand.PrepareAsync(cancellationToken); - - var insertCount = await dbCommand.ExecuteNonQueryAsync(cancellationToken); - - _logger - .LogInformation - ( - "Finished Running {SqlType} Insert on `{Database}.{TableName}`\n\nCommand: {Command}\n\nDocuments Inserted: {DocumentsInserted}", - _sqlConverter.SqlType, - DbConnection.Database, - tableName, - dbCommand.CommandText, - insertCount - ); - } - - public async IAsyncEnumerable Find - ( - IDocumentReader documentReader, - IFilterDefinition filterDefinition, - ISortDefinition? sortDefinition, - int? skip, - int? limit, - TOptions? options, - [EnumeratorCancellation] CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - { - var tableName = TDocument.TableName; - - var dbQuery = _sqlConverter.ConvertQuery(tableName, documentReader, filterDefinition, sortDefinition, skip, limit, options); - - dbQuery.Connection = DbConnection; - dbQuery.Transaction = _dbTransaction; - - _logger - .LogInformation - ( - "Started Enumerating {SqlType} Query on `{Database}.{TableName}`\n\nQuery: {Query}", - _sqlConverter.SqlType, - DbConnection.Database, - tableName, - dbQuery.CommandText - ); - - ulong documentCount = 0; - - await dbQuery.PrepareAsync(cancellationToken); - - await using var reader = await dbQuery.ExecuteReaderAsync(cancellationToken); - - while (await reader.ReadAsync(cancellationToken)) - { - documentCount += 1; - - yield return await documentReader.Read(reader, cancellationToken); - } - - _logger - .LogInformation - ( - "Finished Enumerating {SqlType} Query on `{Database}.{CollectionName}`\n\nQuery: {Query}\n\nDocuments Returned: {DocumentsReturned}", - _sqlConverter.SqlType, - DbConnection.Database, - tableName, - dbQuery.CommandText, - documentCount - ); - } - - public async Task Delete - ( - string tableName, - IFilterDefinition filterDefinition, - CancellationToken cancellationToken - ) - { - AssertNotReadOnly(); - - var dbCommand = _sqlConverter.ConvertDelete(tableName, filterDefinition); - - dbCommand.Connection = DbConnection; - dbCommand.Transaction = _dbTransaction; - - _logger - .LogInformation - ( - "Started Running {SqlType} Delete on `{Database}.{TableName}`\n\nCommand: {Command}", - _sqlConverter.SqlType, - DbConnection.Database, - tableName, - dbCommand.CommandText - ); - - await dbCommand.PrepareAsync(cancellationToken); - - var deletedCount = await dbCommand.ExecuteNonQueryAsync(cancellationToken); - - _logger - .LogInformation( - "Finished Running {SqlType} Delete on `{Database}.{TableName}`\n\nCommand: {Command}\n\nRows Deleted: {DocumentsDeleted}", - _sqlConverter.SqlType, - DbConnection.Database, - tableName, - dbCommand.CommandText, - deletedCount - ); - } - - public override async ValueTask DisposeAsync() - { - if (_dbTransaction != null) - { - await _dbTransaction.DisposeAsync(); - } - - await DbConnection.CloseAsync(); - await DbConnection.DisposeAsync(); - } - - private void AssertNotReadOnly() - { - if (_options.ReadOnly) - { - throw new CannotWriteInReadOnlyModeException(); - } - } - - public async Task StartTransaction(CancellationToken cancellationToken) - { - _dbTransaction = await DbConnection.BeginTransactionAsync(_options.IsolationLevel, cancellationToken); - } - - public async Task CommitTransaction(CancellationToken cancellationToken) - { - await _dbTransaction!.CommitAsync(cancellationToken); - } - - public async Task AbortTransaction(CancellationToken cancellationToken) - { - await _dbTransaction!.RollbackAsync(cancellationToken); - } - - public static ISqlDbSession Create - ( - IServiceProvider serviceProvider, - DbConnection dbConnection, - SqlDbTransactionSessionOptions options - ) - { - return ActivatorUtilities.CreateInstance> - ( - serviceProvider, - dbConnection, - options - ); - } - - public ISqlDbSession WithTransactionSessionOptions(SqlDbTransactionSessionOptions transactionSessionOptions) - { - return new SqlDbSession(_logger, _sqlConverter, transactionSessionOptions, DbConnection); - } -} diff --git a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs b/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs deleted file mode 100644 index 2d488f15..00000000 --- a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using System.Data; -using System.Diagnostics.CodeAnalysis; - -namespace EntityDb.SqlDb.Sessions; - -/// -/// Configuration options for the PostgreSql implementation of . -/// -public sealed class SqlDbTransactionSessionOptions -{ - /// - /// A connection string that is compatible with - /// - public string ConnectionString { get; set; } = default!; - - /// - /// If true, indicates the agent only intends to execute queries. - /// - public bool ReadOnly { get; set; } - - /// - /// If true, indicates the agent can tolerate replication lag for queries. - /// - public bool SecondaryPreferred { get; set; } - - /// - /// Determines the isolation level for transactions. - /// - public IsolationLevel IsolationLevel { get; set; } = IsolationLevel.Snapshot; - - /// - /// Determines how long to wait before a command should be automatically aborted. - /// - public TimeSpan WriteTimeout { get; set; } - - /// - /// Determines how long to wait before a query should be automatically killed. - /// - public TimeSpan? ReadTimeout { get; set; } - - /// - [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] - public override string ToString() - { - return $"{nameof(SqlDbTransactionSessionOptions)}"; - } -} diff --git a/src/EntityDb.SqlDb/Sessions/TestModeSqlDbSession.cs b/src/EntityDb.SqlDb/Sessions/TestModeSqlDbSession.cs deleted file mode 100644 index 6cc90e3f..00000000 --- a/src/EntityDb.SqlDb/Sessions/TestModeSqlDbSession.cs +++ /dev/null @@ -1,72 +0,0 @@ -using EntityDb.Common.Disposables; -using EntityDb.SqlDb.Documents; -using EntityDb.SqlDb.Queries.Definitions.Filter; -using EntityDb.SqlDb.Queries.Definitions.Sort; -using System.Data.Common; - -namespace EntityDb.SqlDb.Sessions; - -internal record TestModeSqlDbSession(ISqlDbSession SqlDbSession) : DisposableResourceBaseRecord, ISqlDbSession - where TOptions : class -{ - public DbConnection DbConnection => SqlDbSession.DbConnection; - - public Task Insert(string tableName, TDocument[] bsonDocuments, CancellationToken cancellationToken) - where TDocument : ITransactionDocument - { - return SqlDbSession.Insert(tableName, bsonDocuments, cancellationToken); - } - - public IAsyncEnumerable Find - ( - IDocumentReader documentReader, - IFilterDefinition filterDefinition, - ISortDefinition? sortDefinition, - int? skip, - int? limit, - TOptions? options, - CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - { - return SqlDbSession.Find - ( - documentReader, - filterDefinition, - sortDefinition, - skip, - limit, - options, - cancellationToken - ); - } - - public Task Delete(string tableName, - IFilterDefinition filterDefinition, CancellationToken cancellationToken) - { - return SqlDbSession.Delete(tableName, filterDefinition, cancellationToken); - } - - public ISqlDbSession WithTransactionSessionOptions(SqlDbTransactionSessionOptions transactionSessionOptions) - { - return this with { SqlDbSession = SqlDbSession.WithTransactionSessionOptions(transactionSessionOptions) }; - } - - public Task StartTransaction(CancellationToken cancellationToken = default) - { - // Test Mode Transactions are started in the Test Mode Repository Factory - return Task.CompletedTask; - } - - public Task CommitTransaction(CancellationToken cancellationToken = default) - { - // Test Mode Transactions are never committed - return Task.CompletedTask; - } - - public Task AbortTransaction(CancellationToken cancellationToken = default) - { - // Test Mode Transactions are aborted in the Test Mode Repository Factory - return Task.CompletedTask; - } -} diff --git a/src/EntityDb.SqlDb/Transactions/ISqlDbTransactionRepositoryFactory.cs b/src/EntityDb.SqlDb/Transactions/ISqlDbTransactionRepositoryFactory.cs deleted file mode 100644 index db2d7263..00000000 --- a/src/EntityDb.SqlDb/Transactions/ISqlDbTransactionRepositoryFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Transactions; - -internal interface ISqlDbTransactionRepositoryFactory : ITransactionRepositoryFactory - where TOptions : class -{ - async Task ITransactionRepositoryFactory.CreateRepository( - string transactionSessionOptionsName, CancellationToken cancellationToken) - { - var options = GetTransactionSessionOptions(transactionSessionOptionsName); - - var sqlDbSession = await CreateSession(options, cancellationToken); - - return CreateRepository(sqlDbSession); - } - - SqlDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName); - - Task> CreateSession(SqlDbTransactionSessionOptions options, - CancellationToken cancellationToken); - - ITransactionRepository CreateRepository(ISqlDbSession sqlDbSession); -} diff --git a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs deleted file mode 100644 index 7966c6bd..00000000 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs +++ /dev/null @@ -1,231 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Commands; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Exceptions; -using EntityDb.SqlDb.Documents.AgentSignature; -using EntityDb.SqlDb.Documents.Command; -using EntityDb.SqlDb.Documents.Lease; -using EntityDb.SqlDb.Documents.Tag; -using EntityDb.SqlDb.Extensions; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Transactions; - -internal class SqlDbTransactionRepository : DisposableResourceBaseClass, ITransactionRepository - where TOptions : class -{ - private readonly IEnvelopeService _envelopeService; - private readonly ISqlDbSession _sqlDbSession; - - public SqlDbTransactionRepository - ( - ISqlDbSession sqlDbSession, - IEnvelopeService envelopeService - ) - { - _sqlDbSession = sqlDbSession; - _envelopeService = envelopeService; - } - - public IAsyncEnumerable EnumerateTransactionIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateTransactionIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateTransactionIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateTransactionIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateTransactionIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return LeaseDocument - .GetQuery(leaseQuery) - .EnumerateTransactionIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateTransactionIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return TagDocument - .GetQuery(tagQuery) - .EnumerateTransactionIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateEntitiesIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateEntityIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return LeaseDocument - .GetQuery(leaseQuery) - .EnumerateEntityIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateEntityIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return TagDocument - .GetQuery(tagQuery) - .EnumerateEntityIds(_sqlDbSession, cancellationToken); - } - - public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateData(_sqlDbSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable EnumerateCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateData(_sqlDbSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return LeaseDocument - .GetQuery(leaseQuery) - .EnumerateData(_sqlDbSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return TagDocument - .GetQuery(tagQuery) - .EnumerateData(_sqlDbSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AgentSignatureDocument - .GetQuery(agentSignatureQuery) - .EnumerateEntitiesAnnotation(_sqlDbSession, _envelopeService, cancellationToken); - } - - public IAsyncEnumerable> EnumerateAnnotatedCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return CommandDocument - .GetQuery(commandQuery) - .EnumerateEntityAnnotation(_sqlDbSession, _envelopeService, cancellationToken); - } - - public async Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default) - { - try - { - await _sqlDbSession.StartTransaction(cancellationToken); - - await PutAgentSignature(transaction, cancellationToken); - - foreach (var transactionCommand in transaction.Subjects) - { - cancellationToken.ThrowIfCancellationRequested(); - - VersionZeroReservedException.ThrowIfZero(transactionCommand.SubjectVersionNumber); - - var previousVersionNumber = await CommandDocument - .GetLastEntityVersionNumber(_sqlDbSession, transactionCommand.SubjectId, cancellationToken); - - OptimisticConcurrencyException.ThrowIfMismatch(previousVersionNumber.Next(), - transactionCommand.SubjectVersionNumber); - - await PutCommand(transaction, transactionCommand, cancellationToken); - } - - await _sqlDbSession.CommitTransaction(cancellationToken); - - return true; - } - catch - { - await _sqlDbSession.AbortTransaction(cancellationToken); - - throw; - } - } - - public override async ValueTask DisposeAsync() - { - await _sqlDbSession.DisposeAsync(); - } - - private async Task PutAgentSignature(ITransaction transaction, CancellationToken cancellationToken) - { - await AgentSignatureDocument - .GetInsert(_envelopeService, transaction) - .Execute(_sqlDbSession, cancellationToken); - } - - private async Task PutCommand(ITransaction transaction, ITransactionCommand transactionCommand, - CancellationToken cancellationToken) - { - await CommandDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand) - .Execute(_sqlDbSession, cancellationToken); - - - if (transactionCommand.AddLeases.Length > 0) - { - await LeaseDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand) - .Execute(_sqlDbSession, cancellationToken); - } - - if (transactionCommand.AddTags.Length > 0) - { - await TagDocument - .GetInsertCommand(_envelopeService, transaction, transactionCommand) - .Execute(_sqlDbSession, cancellationToken); - } - - if (transactionCommand.DeleteLeases.Length > 0) - { - await LeaseDocument - .GetDeleteCommand(transactionCommand) - .Execute(_sqlDbSession, cancellationToken); - } - - if (transactionCommand.DeleteTags.Length > 0) - { - await TagDocument - .GetDeleteCommand(transactionCommand) - .Execute(_sqlDbSession, cancellationToken); - } - } -} diff --git a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepositoryFactoryWrapper.cs b/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepositoryFactoryWrapper.cs deleted file mode 100644 index 3761cf34..00000000 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepositoryFactoryWrapper.cs +++ /dev/null @@ -1,42 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Common.Disposables; -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Transactions; - -internal abstract class SqlDbTransactionRepositoryFactoryWrapper : DisposableResourceBaseClass, - ISqlDbTransactionRepositoryFactory - where TOptions : class -{ - private readonly ISqlDbTransactionRepositoryFactory _sqlDbTransactionRepositoryFactory; - - protected SqlDbTransactionRepositoryFactoryWrapper( - ISqlDbTransactionRepositoryFactory sqlDbTransactionRepositoryFactory) - { - _sqlDbTransactionRepositoryFactory = sqlDbTransactionRepositoryFactory; - } - - public virtual SqlDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName) - { - return _sqlDbTransactionRepositoryFactory.GetTransactionSessionOptions(transactionSessionOptionsName); - } - - public virtual Task> CreateSession(SqlDbTransactionSessionOptions options, - CancellationToken cancellationToken) - { - return _sqlDbTransactionRepositoryFactory.CreateSession(options, cancellationToken); - } - - public virtual ITransactionRepository CreateRepository - ( - ISqlDbSession sqlDbSession - ) - { - return _sqlDbTransactionRepositoryFactory.CreateRepository(sqlDbSession); - } - - public override async ValueTask DisposeAsync() - { - await _sqlDbTransactionRepositoryFactory.DisposeAsync(); - } -} diff --git a/src/EntityDb.SqlDb/Transactions/TestModeSqlDbTransactionRepositoryFactory.cs b/src/EntityDb.SqlDb/Transactions/TestModeSqlDbTransactionRepositoryFactory.cs deleted file mode 100644 index 64b2802b..00000000 --- a/src/EntityDb.SqlDb/Transactions/TestModeSqlDbTransactionRepositoryFactory.cs +++ /dev/null @@ -1,50 +0,0 @@ -using EntityDb.SqlDb.Sessions; - -namespace EntityDb.SqlDb.Transactions; - -internal class - TestModeSqlDbTransactionRepositoryFactory : SqlDbTransactionRepositoryFactoryWrapper - where TOptions : class -{ - private (ISqlDbSession Normal, TestModeSqlDbSession TestMode)? _sessions; - - public TestModeSqlDbTransactionRepositoryFactory( - ISqlDbTransactionRepositoryFactory sqlDbTransactionRepositoryFactory) : base(sqlDbTransactionRepositoryFactory) - { - } - - public override async Task> CreateSession(SqlDbTransactionSessionOptions options, - CancellationToken cancellationToken) - { - if (_sessions.HasValue) - { - return _sessions.Value.TestMode - .WithTransactionSessionOptions(options); - } - - var normalOptions = new SqlDbTransactionSessionOptions - { - ConnectionString = options.ConnectionString - }; - - var normalSession = await base.CreateSession(normalOptions, cancellationToken); - - var testModeSession = new TestModeSqlDbSession(normalSession); - - await normalSession.StartTransaction(cancellationToken); - - _sessions = (normalSession, testModeSession); - - return _sessions.Value.TestMode - .WithTransactionSessionOptions(options); - } - - public override async ValueTask DisposeAsync() - { - if (_sessions.HasValue) - { - await _sessions.Value.Normal.AbortTransaction(); - await _sessions.Value.Normal.DisposeAsync(); - } - } -} diff --git a/src/EntityDb.SqlDb/packages.lock.json b/src/EntityDb.SqlDb/packages.lock.json deleted file mode 100644 index 4aec71b5..00000000 --- a/src/EntityDb.SqlDb/packages.lock.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net7.0": { - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "entitydb.abstractions": { - "type": "Project", - "dependencies": { - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.common": { - "type": "Project", - "dependencies": { - "EntityDb.Abstractions": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.json": { - "type": "Project", - "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/EntityDb.Void/EntityDb.Void.csproj b/src/EntityDb.Void/EntityDb.Void.csproj deleted file mode 100644 index 3ade0c8e..00000000 --- a/src/EntityDb.Void/EntityDb.Void.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - - EntityDb EventSourcing DDD CQRS - An implementation of the EntityDb Transaction Repository interface, specifically made for non-persistent history. - - - - - - - diff --git a/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index 346a3041..00000000 --- a/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Sources; -using EntityDb.Void.Transactions; -using Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Void.Extensions; - -/// -/// Extensions for service collections. -/// -public static class ServiceCollectionExtensions -{ - /// - /// Adds a production-ready implementation of to a service - /// collection. - /// - /// The service collection. - /// - /// This repository does not do anything. - /// - public static void AddVoidTransactions(this IServiceCollection serviceCollection) - { - serviceCollection - .AddSingleton(); - } -} diff --git a/src/EntityDb.Void/Transactions/VoidSourceRepository.cs b/src/EntityDb.Void/Transactions/VoidSourceRepository.cs deleted file mode 100644 index eea3cc67..00000000 --- a/src/EntityDb.Void/Transactions/VoidSourceRepository.cs +++ /dev/null @@ -1,104 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Sources.Subjects; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Disposables; - -namespace EntityDb.Void.Transactions; - -internal sealed class VoidSourceRepository : DisposableResourceBaseClass, ISourceRepository -{ - public IAsyncEnumerable EnumerateSourceIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSourceIds(ISourceSubjectQuery sourceSubjectQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSubjectPointers(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSubjectPointers(ISourceSubjectQuery sourceSubjectQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSubjectPointers(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSubjectPointers(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateSubjects(ISourceSubjectQuery sourceSubjectQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty>(); - } - - public IAsyncEnumerable> EnumerateAnnotatedSubjects(ISourceSubjectQuery sourceSubjectQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty>(); - } - - public Task Put(ISource source, - CancellationToken cancellationToken = default) - { - return Task.FromResult(false); - } -} diff --git a/src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs b/src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs deleted file mode 100644 index daa00cc8..00000000 --- a/src/EntityDb.Void/Transactions/VoidSourceRepositoryFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using EntityDb.Abstractions.Sources; -using EntityDb.Common.Disposables; - -namespace EntityDb.Void.Transactions; - -internal class VoidSourceRepositoryFactory : DisposableResourceBaseClass, ISourceRepositoryFactory -{ - private static readonly Task VoidTransactionRepositoryTask = - Task.FromResult(new VoidSourceRepository() as ISourceRepository); - - public Task CreateRepository( - string sourceSessionOptionsName, CancellationToken cancellationToken = default) - { - return VoidTransactionRepositoryTask.WaitAsync(cancellationToken); - } -} diff --git a/src/EntityDb.Void/packages.lock.json b/src/EntityDb.Void/packages.lock.json deleted file mode 100644 index fd75bd2f..00000000 --- a/src/EntityDb.Void/packages.lock.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net7.0": { - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "entitydb.abstractions": { - "type": "Project", - "dependencies": { - "System.Linq.Async": "[6.0.1, )" - } - }, - "entitydb.common": { - "type": "Project", - "dependencies": { - "EntityDb.Abstractions": "[1.0.0, )", - "System.Linq.Async": "[6.0.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/test/.DS_Store b/test/.DS_Store deleted file mode 100644 index 0c11404434e9e949ae5df53d1450e21fe154b3ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~(P|Sx6o&t)xhRTKinT%!_654&8>}%w3R=PX0Gg)6Vv;SpX(4yH_Z@r%pRZT? zotY6gyNP-wQs%%pXLe@Jzcc@to!OD(`q$&<(w?MU)O_zgW`kI}X-9Tq%>%si80T83 zRHdczPVZEa7)T8KZw%PCd!T_X`D!oJ{C@korgo|^-|`i$CAL19mgl2sIU>roj2CGB z-3ujYpWY=XpTVkhOsUk2xKcGWIs3v)(-y|J7RG^wnzN5wqDw|LBJQQfnPKNVy$7qN zwKW>|tcCPs1JWChF~J7pZCGDgSo<5XZkXZr@!YmB9&f;S$6UtDwcd`iGil)*{TAmh zXXtbHwUN6cO&H4@t@IIgFJk^~VvV&P`&DB3tmu(5>QC$0^f_pvJ|g3mHDi2Cd<|n2 z^S$ckPqq@8v4=8te!lG$E?zNQ2&X=a{fg*U!H00Uc=>Sg%`Vf0@8j?v2{8I*g024#eYXATM diff --git a/test/Directory.Build.props b/test/Directory.Build.props index abdd609b..368f913c 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -13,18 +13,18 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs b/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs index d1316702..7b760a2e 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs @@ -5,4 +5,4 @@ namespace EntityDb.Common.Tests; [CollectionDefinition(nameof(DatabaseContainerCollection))] public class DatabaseContainerCollection : ICollectionFixture { -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs index 6d282c9d..028e554d 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs @@ -1,6 +1,4 @@ -using DotNet.Testcontainers.Builders; -using DotNet.Testcontainers.Configurations; -using DotNet.Testcontainers.Containers; +using Testcontainers.MongoDb; using Testcontainers.Redis; using Xunit; @@ -22,14 +20,12 @@ public class DatabaseContainerFixture : IAsyncLifetime .WithImage("redis:7.2.0") .Build(); - public IContainer MongoDbContainer { get; } = new ContainerBuilder() + public MongoDbContainer MongoDbContainer { get; } = new MongoDbBuilder() .WithImage("mongo:7.0.0") - .WithPortBinding(27017, true) + .WithUsername(null) + .WithPassword(null) .WithBindMount(DockerVolumeMongoDbInit, "/docker-entrypoint-initdb.d") - .WithEnvironment("MONGO_INITDB_ROOT_USERNAME", null) - .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", null) .WithCommand("--replSet", OmniParameter) - .WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil())) .Build(); public async Task InitializeAsync() @@ -43,17 +39,4 @@ public async Task DisposeAsync() await RedisContainer.DisposeAsync(); await MongoDbContainer.DisposeAsync(); } - - private sealed class WaitUntil : IWaitUntil - { - private static readonly string[] LineEndings = { "\r\n", "\n" }; - - public async Task UntilAsync(IContainer container) - { - var (text, text2) = await container.GetLogs(timestampsEnabled: false).ConfigureAwait(false); - return 2.Equals(Array.Empty().Concat(text.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) - .Concat(text2.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)) - .Count(line => line.Contains("Waiting for connections"))); - } - } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs index a15aa262..f4f4c57a 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -1,12 +1,12 @@ -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; -using EntityDb.Common.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Deltas; +using EntityDb.Common.States.Attributes; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; using Microsoft.Extensions.DependencyInjection; using Shouldly; +using System.Diagnostics.CodeAnalysis; using Xunit; using Version = EntityDb.Abstractions.ValueObjects.Version; @@ -16,38 +16,41 @@ namespace EntityDb.Common.Tests.Entities; [SuppressMessage("ReSharper", "UnusedMember.Local")] public class EntityRepositoryTests : TestsBase { - public EntityRepositoryTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base( - serviceProvider, databaseContainerFixture) + public EntityRepositoryTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : + base( + serviceProvider, databaseContainerFixture) { } - private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow(SourcesAdder sourcesAdder, EntityAdder entityAdder) + private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + var entityRepository = await GetReadOnlyEntityRepository(serviceScope, false); - + // ASSERT - Should.Throw(() => entityRepository.Get(default)); + Should.Throw(() => entityRepository.Get(default)); } - private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(SourcesAdder sourcesAdder, EntityAdder entityAdder) + private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); var expectedEntityId = Id.NewId(); @@ -58,7 +61,7 @@ private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpected var entityRepository = await GetWriteEntityRepository(serviceScope, false); entityRepository.Create(expectedEntityId); - + // ARRANGE ASSERTIONS Should.NotThrow(() => entityRepository.Get(expectedEntityId)); @@ -74,30 +77,31 @@ private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpected actualEntityId.ShouldBe(expectedEntityId); } - private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases(SourcesAdder sourcesAdder, EntityAdder entityAdder) + private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity { // ARRANGE var committedSources = new List(); - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); // ACT - entityRepository.Create(default); - - entityRepository.Append(default, new AddLease(new Lease(default!, default!, default!))); + writeRepository.Create(default); - await entityRepository.Commit(default); + writeRepository.Append(default, new AddLease(new Lease(default!, default!, default!))); + + await writeRepository.Commit(); // ASSERT @@ -106,110 +110,112 @@ private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_Th committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); } - private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(SourcesAdder sourcesAdder, EntityAdder entityAdder) + private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity { // ARRANGE - var entityId = Id.NewId(); - - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + await using var readRepository = await GetReadOnlyEntityRepository(serviceScope, false); + + var entityId = Id.NewId(); writeRepository.Create(entityId); writeRepository.Append(entityId, new DoNothing()); - var committed = await writeRepository.Commit(default); - + var committed = await writeRepository.Commit(); + // ARRANGE ASSERTIONS committed.ShouldBeTrue(); - - // ACT - await using var readRepository = await GetReadOnlyEntityRepository(serviceScope, false); + // ACT await readRepository.Load(entityId); // ASSERT - await Should.ThrowAsync(readRepository.Load(entityId)); + await Should.ThrowAsync(readRepository.Load(entityId)); } - private async Task Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements(SourcesAdder sourcesAdder, EntityAdder entityAdder) + private async Task Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity { // ARRANGE var committedSources = new List(); - var numberOfVersionsToTest = 10; - - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); - - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); + + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + + const int numberOfVersionsToTest = 10; + + writeRepository.Create(default); - entityRepository.Create(default); - // ACT for (var i = 1; i <= numberOfVersionsToTest; i++) { - entityRepository.Append(default, new DoNothing()); + writeRepository.Append(default, new DoNothing()); } - await entityRepository.Commit(default); + await writeRepository.Commit(); // ASSERT committedSources.Count.ShouldBe(1); var expectedVersion = Version.Zero; - + for (var i = 1; i <= numberOfVersionsToTest; i++) { expectedVersion = expectedVersion.Next(); - - committedSources[0].Messages[i - 1].EntityPointer.Version.ShouldBe(expectedVersion); + + committedSources[0].Messages[i - 1].StatePointer.Version.ShouldBe(expectedVersion); } } - private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(SourcesAdder sourcesAdder, EntityAdder entityAdder) + private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity { // ARRANGE var committedSources = new List(); - - var expectedDelta = new DoNothing(); - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); - - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - // ACT + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); + var expectedDelta = new DoNothing(); + + // ACT + entityRepository.Create(default); entityRepository.Append(default, expectedDelta); - await entityRepository.Commit(default); + await entityRepository.Commit(); // ASSERT @@ -219,68 +225,74 @@ private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceB } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenEntityNotKnown_WhenGettingEntity_ThenThrow(SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenEntityNotKnown_WhenGettingEntity_ThenThrow(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases(SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows(SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements(SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(SourcesAdder sourcesAdder, EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index ff5e791d..2b92ca32 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -1,19 +1,18 @@ -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; using EntityDb.Common.Polyfills; using EntityDb.Common.Sources.Agents; -using EntityDb.Common.Tests.Implementations.Deltas; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; using EntityDb.Common.Tests.Implementations.Seeders; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.States; using Microsoft.Extensions.DependencyInjection; using Moq; using Shouldly; +using System.Diagnostics.CodeAnalysis; using Xunit; using Version = EntityDb.Abstractions.ValueObjects.Version; @@ -29,52 +28,51 @@ public EntityTests(IServiceProvider serviceProvider, DatabaseContainerFixture da } private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) + where TEntity : class, IEntity { // ARRANGE + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteEntityRepository(serviceScope, true); + await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, true); + const ulong n = 10UL; const ulong m = 5UL; var versionM = new Version(m); - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - var entityId = Id.NewId(); - await using var writeRepository = await GetWriteEntityRepository(serviceScope, true); - writeRepository.Create(entityId); writeRepository.Seed(entityId, m); writeRepository.Seed(entityId, n - m); - - var committed = await writeRepository.Commit(default); - + + var committed = await writeRepository.Commit(); + // ARRANGE ASSERTIONS committed.ShouldBeTrue(); // ACT - await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, true); - await readOnlyRepository.Load(entityId + new Version(m)); - + var entity = readOnlyRepository.Get(entityId); - + // ASSERT - entity.Pointer.Version.ShouldBe(versionM); + entity.GetPointer().Version.ShouldBe(versionM); } - private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetDeltasRuns( - EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEntity_ThenGetDeltasRuns( + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity { // ARRANGE @@ -102,7 +100,8 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T .Returns(ValueTask.CompletedTask); sourceRepositoryMock - .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Setup(repository => + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Returns(() => AsyncEnumerablePolyfill.FromResult(deltas)) .Verifiable(); @@ -110,22 +109,23 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T new Mock(MockBehavior.Strict); sourceRepositoryFactoryMock - .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) + .Setup(factory => factory.Create(It.IsAny(), It.IsAny())) .ReturnsAsync(sourceRepositoryMock.Object); - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - entityAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddSingleton(sourceRepositoryFactoryMock.Object); }); await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, false); writeRepository.Create(entityId); writeRepository.Seed(entityId, expectedVersionNumber); - - var committed = await writeRepository.Commit(default); + + var committed = await writeRepository.Commit(); // ARRANGE ASSERTIONS @@ -133,155 +133,149 @@ private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_T // ACT - await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, false); - await readOnlyRepository.Load(entityId); - + var entity = readOnlyRepository.Get(entityId); // ASSERT - entity.Pointer.Version.ShouldBe(expectedVersion); + entity.GetPointer().Version.ShouldBe(expectedVersion); sourceRepositoryMock .Verify( - repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), + repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), Times.Once); } private async Task - Generic_GivenNoSnapshotRepositoryFactory_WhenCreatingEntityRepository_ThenNoSnapshotRepository( - EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + Generic_GivenNoStateRepositoryFactory_WhenCreatingEntityRepository_ThenNoStateRepository( + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - entityAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); }); // ACT - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetService>(); + await using var stateRepositoryFactory = serviceScope.ServiceProvider + .GetService>(); await using var entityRepository = await GetReadOnlyEntityRepository(serviceScope, true); // ASSERT - snapshotRepositoryFactory.ShouldBeNull(); + stateRepositoryFactory.ShouldBeNull(); - entityRepository.SnapshotRepository.ShouldBeNull(); + entityRepository.StateRepository.ShouldBeNull(); } private async Task - Generic_GivenNoSnapshotSessionOptions_WhenCreatingEntityRepository_ThenNoSnapshotRepository( - EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + Generic_GivenNoStateSessionOptions_WhenCreatingEntityRepository_ThenNoStateRepository( + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - entityAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); - serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedStateRepositoryFactory()); }); // ACT - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetService>(); + await using var stateRepositoryFactory = serviceScope.ServiceProvider + .GetService>(); await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); // ASSERT - snapshotRepositoryFactory.ShouldNotBeNull(); + stateRepositoryFactory.ShouldNotBeNull(); - entityRepository.SnapshotRepository.ShouldBeNull(); + entityRepository.StateRepository.ShouldBeNull(); } private async Task - Generic_GivenSnapshotRepositoryFactoryAndSnapshotSessionOptions_WhenCreatingEntityRepository_ThenNoSnapshotRepository< - TEntity>(EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + Generic_GivenStateRepositoryFactoryAndStateSessionOptions_WhenCreatingEntityRepository_ThenNoStateRepository< + TEntity>(EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - entityAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); - serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedStateRepositoryFactory()); }); // ACT - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetService>(); + await using var stateRepositoryFactory = serviceScope.ServiceProvider + .GetService>(); await using var entityRepository = await GetReadOnlyEntityRepository(serviceScope, true); // ASSERT - snapshotRepositoryFactory.ShouldNotBeNull(); + stateRepositoryFactory.ShouldNotBeNull(); - entityRepository.SnapshotRepository.ShouldNotBeNull(); + entityRepository.StateRepository.ShouldNotBeNull(); } private async Task - Generic_GivenSnapshotAndNewDeltas_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( - EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + Generic_GivenPersistedStateAndNewDeltas_WhenGettingCurrentState_ThenReturnNewerThanPersistedState( + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity { // ARRANGE - var snapshot = TEntity.Construct(default).WithVersion(new Version(1)); + var previousEntity = TEntity.Construct(default(Id) + new Version(1)); - var newDeltas = new object[] - { - new DoNothing(), - new DoNothing(), - }; + var newDeltas = new object[] { new DoNothing(), new DoNothing() }; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - entityAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(newDeltas)); - serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory(snapshot)); + serviceCollection.AddSingleton(GetMockedStateRepositoryFactory(previousEntity)); }); - // ACT - await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + // ACT + await entityRepository.Load(default); - - var entity = entityRepository.Get(default); + + var currentEntity = entityRepository.Get(default); // ASSERT - entity.ShouldNotBe(snapshot); - entity.Pointer.Version.ShouldBe( - new Version(snapshot.Pointer.Version.Value + Convert.ToUInt64(newDeltas.Length))); + currentEntity.ShouldNotBe(previousEntity); + currentEntity.GetPointer().Version.ShouldBe( + new Version(previousEntity.GetPointer().Version.Value + Convert.ToUInt64(newDeltas.Length))); } private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow( - EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - entityAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); }); @@ -290,25 +284,25 @@ private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_The // ASSERT - Should.Throw(() => entityRepository.Get(default)); + Should.Throw(() => entityRepository.Get(default)); } - + private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity( - SourcesAdder sourcesAdder, EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity { // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => + + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entityAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - + var entityId = Id.NewId(); - - var expectedEntity = TEntity.Construct(entityId).WithVersion(new Version(1)); - + + var expectedEntity = TEntity.Construct(entityId + new Version(1)); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); var source = new Source @@ -316,126 +310,120 @@ private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEnti Id = Id.NewId(), TimeStamp = TimeStamp.UtcNow, AgentSignature = new UnknownAgentSignature(new Dictionary()), - Messages = new[] - { - new Message - { - Id = Id.NewId(), - EntityPointer = entityId, - Delta = new DoNothing(), - } - }.ToImmutableArray() + Messages = new[] { new Message { Id = Id.NewId(), StatePointer = entityId, Delta = new DoNothing() } }, }; - + var committed = await entityRepository.SourceRepository.Commit(source); - + // ARRANGE ASSERTIONS - + committed.ShouldBeTrue(); - + // ACT await entityRepository.Load(entityId); - + var actualEntity = entityRepository.Get(entityId); - + // ASSERT - + actualEntity.ShouldBeEquivalentTo(expectedEntity); } [Theory] - [MemberData(nameof(AddSourcesAndEntitySnapshots))] - public Task GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM(SourcesAdder sourcesAdder, - SnapshotAdder entitySnapshotAdder) + [MemberData(nameof(With_Source_EntityState))] + public Task GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( + SourceRepositoryAdder sourceRepositoryAdder, + StateRepositoryAdder entityStateRepositoryAdder) { return RunGenericTestAsync ( - new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { sourcesAdder, entitySnapshotAdder } + new[] { entityStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetDeltasRuns(EntityAdder entityAdder) + [MemberData(nameof(With_Entity))] + public Task GivenExistingEntityWithNoPersistedState_WhenGettingEntity_ThenGetDeltasRuns( + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenNoSnapshotRepositoryFactory_WhenCreatingEntityRepository_ThenNoSnapshotRepository( - EntityAdder entityAdder) + [MemberData(nameof(With_Entity))] + public Task GivenNoStateRepositoryFactory_WhenCreatingEntityRepository_ThenNoStateRepository( + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenNoSnapshotSessionOptions_WhenCreatingEntityRepository_ThenNoSnapshotRepository( - EntityAdder entityAdder) + [MemberData(nameof(With_Entity))] + public Task GivenNoStateSessionOptions_WhenCreatingEntityRepository_ThenNoStateRepository( + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] + [MemberData(nameof(With_Entity))] public Task - GivenSnapshotRepositoryFactoryAndSnapshotSessionOptions_WhenCreatingEntityRepository_ThenNoSnapshotRepository( - EntityAdder entityAdder) + GivenStateRepositoryFactoryAndStateSessionOptions_WhenCreatingEntityRepository_ThenNoStateRepository( + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenSnapshotAndNewDeltas_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( - EntityAdder entityAdder) + [MemberData(nameof(With_Entity))] + public Task GivenPersistedStateAndNewDeltas_WhenGettingCurrentState_ThenReturnNewerThanPersistedState( + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow(EntityAdder entityAdder) + [MemberData(nameof(With_Entity))] + public Task GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow(EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesAndEntity))] - public Task GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity(SourcesAdder sourcesAdder, - EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { sourcesAdder, entityAdder } + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj index f1872e80..6d38424f 100644 --- a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj +++ b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj @@ -7,8 +7,8 @@ - - + + @@ -17,4 +17,5 @@ + diff --git a/test/EntityDb.Common.Tests/Envelopes/EnvelopeTestsBase.cs b/test/EntityDb.Common.Tests/Envelopes/EnvelopeTestsBase.cs index 10f5f284..c05a0641 100644 --- a/test/EntityDb.Common.Tests/Envelopes/EnvelopeTestsBase.cs +++ b/test/EntityDb.Common.Tests/Envelopes/EnvelopeTestsBase.cs @@ -16,11 +16,12 @@ protected EnvelopeTestsBase(IServiceProvider startupServiceProvider) : base(star protected abstract TEnvelopeValue GenerateCorruptedSerializedData(); [Fact] - public void GivenValidRecord_WhenDeconstructedSerializedAndDeserialized_ThenReconstructReturnsEquivalentRecord() + public async Task + GivenValidRecord_WhenDeconstructedSerializedAndDeserialized_ThenReconstructReturnsEquivalentRecord() { // ARRANGE - using var serviceScope = CreateServiceScope(); + await using var serviceScope = CreateServiceScope(); var envelopeService = serviceScope.ServiceProvider .GetRequiredService>(); @@ -43,11 +44,11 @@ public void GivenValidRecord_WhenDeconstructedSerializedAndDeserialized_ThenReco } [Fact] - public void WhenDeserializingCorruptedEnvelope_ThrowDeserializeException() + public async Task WhenDeserializingCorruptedEnvelope_ThrowDeserializeException() { // ARRANGE - using var serviceScope = CreateServiceScope(); + await using var serviceScope = CreateServiceScope(); var envelopeService = serviceScope.ServiceProvider .GetRequiredService>(); @@ -56,22 +57,25 @@ public void WhenDeserializingCorruptedEnvelope_ThrowDeserializeException() // ASSERT - Should.Throw(() => { envelopeService.Deserialize(corruptedSerializedData); }); + Should.Throw(() => + { + envelopeService.Deserialize(corruptedSerializedData); + }); } [Fact] - public void WhenSerializingNull_ThrowSerializeException() + public async Task WhenSerializingNull_ThrowSerializeException() { // ARRANGE - using var serviceScope = CreateServiceScope(); + await using var serviceScope = CreateServiceScope(); var envelopeService = serviceScope.ServiceProvider .GetRequiredService>(); // ASSERT - Should.Throw(() => { envelopeService.Serialize(null); }); + Should.Throw(() => { envelopeService.Serialize(null); }); } private interface IRecord @@ -79,4 +83,4 @@ private interface IRecord } protected record TestRecord(TTestProperty TestProperty) : IRecord; -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs b/test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs deleted file mode 100644 index 823d8760..00000000 --- a/test/EntityDb.Common.Tests/EventStreams/EventStreamTests.cs +++ /dev/null @@ -1,335 +0,0 @@ -using EntityDb.Abstractions.EventStreams; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.EventStreams; -using EntityDb.Common.Extensions; -using EntityDb.Common.Polyfills; -using EntityDb.Common.Sources.Queries.Standard; -using EntityDb.Common.Tests.Implementations.Deltas; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Shouldly; -using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.Common.Tests.EventStreams; - -[Collection(nameof(DatabaseContainerCollection))] -public class EventStreamTests : TestsBase -{ - public EventStreamTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) - : base(serviceProvider, databaseContainerFixture) - { - } - - [Fact] - public async Task GivenNewEventStreamMock_WhenStagingNewEventKey_ThenCommittedSourceIsCorrect() - { - // ARRANGE - - var streamKey = new Key("StreamKey"); - var expectedStreamKeyLease = EventStreamRepository.GetStreamKeyLease(streamKey); - - var eventKey = new Key("EventKey"); - var expectedEventKeyLease = EventStreamRepository.GetEventKeyLease(streamKey, eventKey); - - var expectedDelta = new DoNothing(); - - var committedSources = new List(); - - var sequenceMock = new MockSequence(); - var sourceRepositoryMock = new Mock(MockBehavior.Strict); - - // First query checks if eventKey lease already exists - sourceRepositoryMock - .InSequence(sequenceMock) - .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerable.Empty()); - - // Second query checks if streamKey lease already exists - sourceRepositoryMock - .InSequence(sequenceMock) - .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerable.Empty()); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock, committedSources)); - - serviceCollection.AddEventStream(); - }); - - await using var eventStreamRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(default!, default!); - - // ACT - - var staged = await eventStreamRepository - .Stage(streamKey, eventKey, expectedDelta); - - var committed = await eventStreamRepository - .Commit(Id.NewId()); - - // ASSERT - - staged.ShouldBeTrue(); - committed.ShouldBeTrue(); - - committedSources.Count.ShouldBe(1); - committedSources[0].Messages.Length.ShouldBe(1); - committedSources[0].Messages[0].EntityPointer.Version.ShouldBe(Version.Zero); - committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); - committedSources[0].Messages[0].AddLeases.Length.ShouldBe(2); - committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedEventKeyLease); - committedSources[0].Messages[0].AddLeases[1].ShouldBe(expectedStreamKeyLease); - } - - [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenNewEventStream_WhenStagingNewEventKey_ThenCommitReturnsTrue(SourcesAdder sourcesAdder) - { - // ARRANGE - - var streamKey = new Key("StreamKey"); - var streamKeyLease = EventStreamRepository.GetStreamKeyLease(streamKey); - - var eventKey = new Key("EventKey"); - var eventKeyLease = EventStreamRepository.GetEventKeyLease(streamKey, eventKey); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddEventStream(); - }); - - await using var eventStreamRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(default!, default!); - - // ACT - - var staged = await eventStreamRepository - .Stage(streamKey, eventKey, new DoNothing()); - - var committed = await eventStreamRepository - .Commit(Id.NewId()); - - var entityPointerCount = await eventStreamRepository.SourceRepository - .EnumerateEntityPointers(new MatchingLeasesQuery(streamKeyLease, eventKeyLease)) - .CountAsync(); - - // ASSERT - - staged.ShouldBeTrue(); - committed.ShouldBeTrue(); - entityPointerCount.ShouldBe(1); - } - - [Fact] - public async Task GivenExistingEventStreamMock_WhenStagingNewEventKey_ThenCommittedSourceIsCorrect() - { - // ARRANGE - - var entityPointer = Id.NewId() + Version.Zero.Next(); - var streamKey = new Key("StreamKey"); - var eventKey = new Key("EventKey"); - var expectedLease = EventStreamRepository.GetEventKeyLease(streamKey, eventKey); - - var committedSources = new List(); - - var sequenceMock = new MockSequence(); - var sourceRepositoryMock = new Mock(MockBehavior.Strict); - - // First query checks if eventKey lease already exists - sourceRepositoryMock - .InSequence(sequenceMock) - .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerable.Empty()); - - // Second query checks if streamKey lease already exists - sourceRepositoryMock - .InSequence(sequenceMock) - .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerablePolyfill.FromResult(new[] { entityPointer })); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock, committedSources)); - - serviceCollection.AddEventStream(); - }); - - await using var eventStreamRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(default!, default!); - - // ARRANGE ASSERTIONS - - entityPointer.Version.ShouldNotBe(Version.Zero); - - // ACT - - var staged = await eventStreamRepository - .Stage(streamKey, eventKey, new DoNothing()); - - var committed = await eventStreamRepository - .Commit(Id.NewId()); - - // ASSERT - - staged.ShouldBeTrue(); - committed.ShouldBeTrue(); - - committedSources.Count.ShouldBe(1); - committedSources[0].Messages.Length.ShouldBe(1); - committedSources[0].Messages[0].EntityPointer.Id.ShouldBe(entityPointer.Id); - committedSources[0].Messages[0].EntityPointer.Version.ShouldBe(Version.Zero); - committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); - committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedLease); - } - - [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenExistingEventStream_WhenStagingNewEventKey_ThenCommitReturnsTrue(SourcesAdder sourcesAdder) - { - // ARRANGE - - var streamKey = new Key("StreamKey"); - var streamKeyLease = EventStreamRepository.GetStreamKeyLease(streamKey); - - var eventKey1 = new Key("EventKey1"); - var eventKeyLease1 = EventStreamRepository.GetEventKeyLease(streamKey, eventKey1); - - var eventKey2 = new Key("EventKey2"); - var eventKeyLease2 = EventStreamRepository.GetEventKeyLease(streamKey, eventKey2); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddEventStream(); - }); - - await using var eventStreamRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(default!, default!); - - var firstStaged = await eventStreamRepository - .Stage(streamKey, eventKey1, new DoNothing()); - - var firstCommitted = await eventStreamRepository - .Commit(Id.NewId()); - - // ARRANGE ASSERTIONS - - firstStaged.ShouldBeTrue(); - firstCommitted.ShouldBeTrue(); - - // ACT - - var secondStaged = await eventStreamRepository - .Stage(streamKey, eventKey2, new DoNothing()); - - var secondCommitted = await eventStreamRepository - .Commit(Id.NewId()); - - var entityPointerCount = await eventStreamRepository.SourceRepository - .EnumerateEntityPointers(new MatchingLeasesQuery(streamKeyLease, eventKeyLease1, eventKeyLease2)) - .CountAsync(); - - // ASSERT - - secondStaged.ShouldBeTrue(); - secondCommitted.ShouldBeTrue(); - entityPointerCount.ShouldBe(2); - } - - [Fact] - public async Task GivenExistingEventStreamMock_WhenStagingDuplicateEventKey_ThenStageReturnsFalse() - { - // ARRANGE - - var entityPointer = Id.NewId() + Version.Zero.Next(); - var streamKey = new Key("StreamKey"); - var eventKey = new Key("EventKey"); - - var sequenceMock = new MockSequence(); - var sourceRepositoryMock = new Mock(MockBehavior.Strict); - - // First query checks if eventKey lease already exists - sourceRepositoryMock - .InSequence(sequenceMock) - .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerablePolyfill.FromResult(new[] { entityPointer })); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock)); - - serviceCollection.AddEventStream(); - }); - - await using var eventStreamRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(default!, default!); - - // ACT - - var staged = await eventStreamRepository - .Stage(streamKey, eventKey, new DoNothing()); - - // ASSERT - - staged.ShouldBeFalse(); - } - - [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenExistingEventStream_WhenStagingDuplicateEventKey_ThenStagedReturnsFalse( - SourcesAdder sourcesAdder) - { - // ARRANGE - - var streamKey = new Key("StreamKey"); - var eventKey = new Key("EventKey"); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddEventStream(); - }); - - await using var eventStreamRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(default!, default!); - - var stagedOnce = await eventStreamRepository - .Stage(streamKey, eventKey, new DoNothing()); - - var committedOnce = await eventStreamRepository - .Commit(Id.NewId()); - - // ARRANGE ASSERTIONS - - stagedOnce.ShouldBeTrue(); - committedOnce.ShouldBeTrue(); - - // ACT - - var stagedTwice = await eventStreamRepository - .Stage(streamKey, eventKey, new DoNothing()); - - // ASSERT - - stagedTwice.ShouldBeFalse(); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs index 610a3046..862c4d4c 100644 --- a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs +++ b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs @@ -197,4 +197,4 @@ public void XnorTruthTable() private record SingleInput(bool Input1, bool ExpectedOutput); private record DoubleInput(bool Input1, bool Input2, bool ExpectedOutput); -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/IStartup.cs b/test/EntityDb.Common.Tests/IStartup.cs index c8d8e108..67993c61 100644 --- a/test/EntityDb.Common.Tests/IStartup.cs +++ b/test/EntityDb.Common.Tests/IStartup.cs @@ -5,4 +5,4 @@ namespace EntityDb.Common.Tests; public interface IStartup { void AddServices(IServiceCollection serviceCollection); -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs deleted file mode 100644 index 657657b2..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Deltas/AddLease.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Entities; - -namespace EntityDb.Common.Tests.Implementations.Deltas; - -public record AddLease(ILease Lease) : DoNothing, IAddLeasesDelta -{ - public IEnumerable GetLeases(TestEntity entity) - { - yield return Lease; - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs deleted file mode 100644 index 96788805..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Deltas/AddTag.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Entities; - -namespace EntityDb.Common.Tests.Implementations.Deltas; - -public record AddTag(ITag Tag) : DoNothing, IAddTagsDelta -{ - public IEnumerable GetTags(TestEntity entity) - { - yield return Tag; - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs deleted file mode 100644 index 2a4b6d92..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteLease.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Entities; - -namespace EntityDb.Common.Tests.Implementations.Deltas; - -public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesDelta -{ - public IEnumerable GetLeases(TestEntity entity) - { - yield return Lease; - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs b/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs deleted file mode 100644 index 329b3d93..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Deltas/DeleteTag.cs +++ /dev/null @@ -1,13 +0,0 @@ -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Entities; - -namespace EntityDb.Common.Tests.Implementations.Deltas; - -public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsDelta -{ - public IEnumerable GetTags(TestEntity entity) - { - yield return Tag; - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs new file mode 100644 index 00000000..3e699a05 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public record AddLease(ILease Lease) : DoNothing, IAddLeasesDelta +{ + public IEnumerable GetLeases(TestEntity state) + { + yield return Lease; + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs new file mode 100644 index 00000000..248f56bf --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public record AddTag(ITag Tag) : DoNothing, IAddTagsDelta +{ + public IEnumerable GetTags(TestEntity state) + { + yield return Tag; + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs new file mode 100644 index 00000000..8498c22f --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesDelta +{ + public IEnumerable GetLeases(TestEntity state) + { + yield return Lease; + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs new file mode 100644 index 00000000..a06d882b --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsDelta +{ + public IEnumerable GetTags(TestEntity state) + { + yield return Tag; + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs similarity index 70% rename from test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs rename to test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs index 3d822364..84e2de6f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Deltas/DoNothing.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs @@ -1,8 +1,7 @@ -using EntityDb.Abstractions.Snapshots.Transforms; -using EntityDb.Common.Tests.Implementations.Entities; +using EntityDb.Abstractions.States.Transforms; using EntityDb.Common.Tests.Implementations.Projections; -namespace EntityDb.Common.Tests.Implementations.Deltas; +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; public record DoNothing : IReducer, IMutator { @@ -15,4 +14,4 @@ public TestEntity Reduce(TestEntity entity) { return new TestEntity { Pointer = entity.Pointer.Next() }; } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs similarity index 54% rename from test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs rename to test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs index ab6b24c8..299ffa3b 100644 --- a/test/EntityDb.Common.Tests/Implementations/Deltas/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs @@ -1,22 +1,20 @@ -using EntityDb.Abstractions.Entities.Deltas; -using EntityDb.Abstractions.Snapshots.Transforms; -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Entities; -using EntityDb.Common.Tests.Implementations.Leases; +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; +using EntityDb.Abstractions.States.Transforms; using EntityDb.Common.Tests.Implementations.Projections; -using EntityDb.Common.Tests.Implementations.Tags; +using EntityDb.Common.Tests.Implementations.States.Attributes; -namespace EntityDb.Common.Tests.Implementations.Deltas; +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; public record StoreNumber(ulong Number) : IReducer, IMutator, IAddLeasesDelta, IAddTagsDelta { - public IEnumerable GetLeases(TestEntity entity) + public IEnumerable GetLeases(TestEntity state) { yield return new CountLease(Number); } - public IEnumerable GetTags(TestEntity entity) + public IEnumerable GetTags(TestEntity state) { yield return new CountTag(Number); } @@ -30,4 +28,4 @@ public TestEntity Reduce(TestEntity entity) { return new TestEntity { Pointer = entity.Pointer.Next() }; } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index c1142f4f..53d7aab6 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,19 +1,18 @@ using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots.Transforms; +using EntityDb.Abstractions.States.Transforms; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.States; using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Entities; -public record TestEntity : IEntity, ISnapshotWithTestLogic +public record TestEntity : IEntity, IStateWithTestLogic { + public required Pointer Pointer { get; init; } + public static TestEntity Construct(Pointer pointer) { - return new TestEntity - { - Pointer = pointer, - }; + return new TestEntity { Pointer = pointer }; } public Pointer GetPointer() @@ -28,7 +27,10 @@ public static bool CanReduce(object delta) public TestEntity Reduce(object delta) { - if (delta is IReducer reducer) return reducer.Reduce(this); + if (delta is IReducer reducer) + { + return reducer.Reduce(this); + } throw new NotSupportedException(); } @@ -38,23 +40,21 @@ public bool ShouldRecord() return ShouldRecordLogic.Value is not null && ShouldRecordLogic.Value.Invoke(this); } - public bool ShouldRecordAsLatest(TestEntity? previousMostRecentSnapshot) + public bool ShouldRecordAsLatest(TestEntity? previousLatestState) { return ShouldRecordAsLatestLogic.Value is not null && - ShouldRecordAsLatestLogic.Value.Invoke(this, previousMostRecentSnapshot); + ShouldRecordAsLatestLogic.Value.Invoke(this, previousLatestState); } - public required Pointer Pointer { get; init; } - public static string MongoDbCollectionName => "TestEntities"; public static string RedisKeyNamespace => "test-entity"; + public static AsyncLocal?> ShouldRecordLogic { get; } = new(); + + public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); + public TestEntity WithVersion(Version version) { return new TestEntity { Pointer = Pointer.Id + version }; } - - public static AsyncLocal?> ShouldRecordLogic { get; } = new(); - - public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index b7ce4702..6f20874b 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,26 +1,25 @@ -using System.Runtime.CompilerServices; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Snapshots.Transforms; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States.Transforms; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Extensions; +using EntityDb.Common.Sources; using EntityDb.Common.Sources.Queries.Standard; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.States; using Microsoft.Extensions.DependencyInjection; +using System.Runtime.CompilerServices; using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Projections; -public class OneToOneProjection : IProjection, ISnapshotWithTestLogic +public class OneToOneProjection : IProjection, IStateWithTestLogic { public TimeStamp LastSourceAt { get; set; } + public required Pointer Pointer { get; set; } + public static OneToOneProjection Construct(Pointer pointer) { - return new OneToOneProjection - { - Pointer = pointer, - }; + return new OneToOneProjection { Pointer = pointer }; } public Pointer GetPointer() @@ -34,11 +33,14 @@ public void Mutate(Source source) foreach (var message in source.Messages) { - if (message.Delta is not IMutator mutator) continue; + if (message.Delta is not IMutator mutator) + { + continue; + } mutator.Mutate(this); - Pointer = message.EntityPointer; + Pointer = message.StatePointer; } } @@ -47,10 +49,10 @@ public bool ShouldRecord() return ShouldRecordLogic.Value is not null && ShouldRecordLogic.Value.Invoke(this); } - public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) + public bool ShouldRecordAsLatest(OneToOneProjection? previousLatestState) { return ShouldRecordAsLatestLogic.Value is not null && - ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); + ShouldRecordAsLatestLogic.Value.Invoke(this, previousLatestState); } public async IAsyncEnumerable EnumerateSources @@ -64,39 +66,39 @@ [EnumeratorCancellation] CancellationToken cancellationToken var sourceRepository = await serviceProvider .GetRequiredService() - .CreateRepository(TestSessionOptions.ReadOnly, cancellationToken); + .Create(TestSessionOptions.ReadOnly, cancellationToken); var sourceIds = await sourceRepository .EnumerateSourceIds(query, cancellationToken) .ToArrayAsync(cancellationToken); foreach (var sourceId in sourceIds) + { yield return await sourceRepository .GetSource(sourceId, cancellationToken); + } } - public static IEnumerable EnumerateEntityIds(Source source) + public static IEnumerable EnumerateRelevantStateIds(Source source) { return source.Messages .Where(message => message.Delta is IMutator) - .Select(message => message.EntityPointer.Id) + .Select(message => message.StatePointer.Id) .Distinct(); } - public required Pointer Pointer { get; set; } - public static string MongoDbCollectionName => "OneToOneProjections"; public static string RedisKeyNamespace => "one-to-one-projection"; + public static AsyncLocal?> ShouldRecordLogic { get; } = new(); + + public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = + new(); + public OneToOneProjection WithVersion(Version version) { Pointer = Pointer.Id + version; return this; } - - public static AsyncLocal?> ShouldRecordLogic { get; } = new(); - - public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = - new(); -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs deleted file mode 100644 index 74e1f6fa..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityQuery.cs +++ /dev/null @@ -1,66 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Tests.Implementations.Queries; - -public record EntityQuery(Id EntityId, object? Options = null) : IMessageGroupQuery, IMessageQuery, - ILeaseQuery, ITagQuery -{ - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(ILeaseSortBuilder builder) - { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersion(true) - ); - } - - public TFilter GetFilter(IMessageGroupFilterBuilder builder) - { - return builder.AnyEntityIdIn(EntityId); - } - - public TSort GetSort(IMessageGroupSortBuilder builder) - { - return builder.EntityIds(true); - } - - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(IMessageFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(IMessageSortBuilder builder) - { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersion(true) - ); - } - - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(ITagSortBuilder builder) - { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersion(true) - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs deleted file mode 100644 index dc195f3a..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityVersionQuery.cs +++ /dev/null @@ -1,56 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.Common.Tests.Implementations.Queries; - -public record EntityVersionQuery(Version Gte, Version Lte, object? Options = null) : IMessageQuery, - ILeaseQuery, ITagQuery -{ - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.And - ( - builder.EntityVersionGte(Gte), - builder.EntityVersionLte(Lte) - ); - } - - public TSort GetSort(ILeaseSortBuilder builder) - { - return builder.EntityVersion(true); - } - - public TFilter GetFilter(IMessageFilterBuilder builder) - { - return builder.And - ( - builder.EntityVersionGte(Gte), - builder.EntityVersionLte(Lte) - ); - } - - public TSort GetSort(IMessageSortBuilder builder) - { - return builder.EntityVersion(true); - } - - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.And - ( - builder.EntityVersionGte(Gte), - builder.EntityVersionLte(Lte) - ); - } - - public TSort GetSort(ITagSortBuilder builder) - { - return builder.EntityVersion(true); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs deleted file mode 100644 index dc2de17e..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/DeltaSeeder.cs +++ /dev/null @@ -1,85 +0,0 @@ -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Tests.Implementations.Deltas; - -namespace EntityDb.Common.Tests.Implementations.Seeders; - -public interface IDeltaSeeder -{ - object Create(ulong versionNumber); -} - -public class AddTagSeeder : IDeltaSeeder -{ - private readonly ITag _tag; - - public AddTagSeeder(ITag tag) - { - _tag = tag; - } - - public object Create(ulong versionNumber) - { - return new AddTag(_tag); - } -} - -public class DeleteTagSeeder : IDeltaSeeder -{ - private readonly ITag _tag; - - public DeleteTagSeeder(ITag tag) - { - _tag = tag; - } - - public object Create(ulong versionNumber) - { - return new DeleteTag(_tag); - } -} - -public class AddLeaseSeeder : IDeltaSeeder -{ - private readonly ILease _lease; - - public AddLeaseSeeder(ILease lease) - { - _lease = lease; - } - - public object Create(ulong versionNumber) - { - return new AddLease(_lease); - } -} - -public class DeleteLeaseSeeder : IDeltaSeeder -{ - private readonly ILease _lease; - - public DeleteLeaseSeeder(ILease lease) - { - _lease = lease; - } - - public object Create(ulong versionNumber) - { - return new DeleteLease(_lease); - } -} - -public class StoreNumberSeeder : IDeltaSeeder -{ - public object Create(ulong versionNumber) - { - return new StoreNumber(versionNumber); - } -} - -public static class DeltaSeeder -{ - public static object Create() - { - return new DoNothing(); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs index 261c5d43..b7aa2075 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Tests.Implementations.Deltas; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; namespace EntityDb.Common.Tests.Implementations.Seeders; @@ -13,11 +12,11 @@ public static void Seed Id entityId, ulong numDeltas ) - where TEntity : class, IEntity, ISnapshotWithTestLogic + where TEntity : class, IEntity { for (ulong i = 0; i < numDeltas; i++) { entityRepository.Append(entityId, new DoNothing()); } } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs index 6abb1195..125707c7 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Common.States.Attributes; namespace EntityDb.Common.Tests.Implementations.Seeders; @@ -9,4 +9,4 @@ public static ILease Create() { return new Lease("Foo", "Bar", "Baz"); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs deleted file mode 100644 index 022883fe..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/MessageSeeder.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Tests.Implementations.Snapshots; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.Common.Tests.Implementations.Seeders; - -public static class MessageSeeder -{ - public static IEnumerable CreateFromDeltas(Id entityId, uint numDeltas, - ulong previousVersionValue = 0) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - var previousVersion = new Version(previousVersionValue); - - for (var versionOffset = 0; versionOffset < numDeltas; versionOffset++) - { - previousVersion = previousVersion.Next(); - - yield return new Message - { - Id = Id.NewId(), - EntityPointer = entityId + previousVersion, - Delta = DeltaSeeder.Create(), - }; - } - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs deleted file mode 100644 index 1aa65866..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/SourceSeeder.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Immutable; -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Sources.Agents; -using EntityDb.Common.Tests.Implementations.Snapshots; - -namespace EntityDb.Common.Tests.Implementations.Seeders; - -public static class SourceSeeder -{ - public static Source Create(params Message[] messages) - { - return new Source - { - Id = Id.NewId(), - TimeStamp = TimeStamp.UtcNow, - AgentSignature = new UnknownAgentSignature(new Dictionary()), - Messages = messages.ToImmutableArray(), - }; - } - - public static Source Create(Id entityId, uint numDeltas, - ulong previousVersionValue = 0) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - var messages = MessageSeeder - .CreateFromDeltas(entityId, numDeltas, previousVersionValue) - .ToArray(); - - return Create(messages); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs index 03ebec8e..0c751b1e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Sources.Attributes; -using EntityDb.Common.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; +using EntityDb.Common.States.Attributes; namespace EntityDb.Common.Tests.Implementations.Seeders; @@ -9,4 +9,4 @@ public static ITag Create() { return new Tag("Foo", "Bar"); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs deleted file mode 100644 index a7917432..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ /dev/null @@ -1,16 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.Common.Tests.Implementations.Snapshots; - -public interface ISnapshotWithTestLogic : ISnapshot - where TSnapshot : class -{ - Pointer Pointer { get; } - static abstract string MongoDbCollectionName { get; } - static abstract string RedisKeyNamespace { get; } - static abstract AsyncLocal?> ShouldRecordLogic { get; } - static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } - TSnapshot WithVersion(Version version); -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountQuery.cs similarity index 81% rename from test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountQuery.cs index a5e7d1a3..f7d80f8f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountQuery.cs @@ -1,10 +1,9 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Tests.Implementations.Leases; -using EntityDb.Common.Tests.Implementations.Tags; +using EntityDb.Common.Tests.Implementations.States.Attributes; -namespace EntityDb.Common.Tests.Implementations.Queries; +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; public record CountQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseQuery, ITagQuery { @@ -12,7 +11,7 @@ public record CountQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseQ public int? Take => null; - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.And ( @@ -35,7 +34,7 @@ public TSort GetSort(ILeaseSortBuilder builder) ); } - public TFilter GetFilter(ITagFilterBuilder builder) + public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.And ( @@ -57,4 +56,4 @@ public TSort GetSort(ITagSortBuilder builder) builder.TagValue(true) ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/SourceIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdQuery.cs similarity index 58% rename from test/EntityDb.Common.Tests/Implementations/Queries/SourceIdQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdQuery.cs index 666b70b5..5980fb49 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/SourceIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdQuery.cs @@ -3,12 +3,12 @@ using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Common.Tests.Implementations.Queries; +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceIdQuery(Id SourceId, object? Options = null) : IMessageGroupQuery, IMessageQuery, ILeaseQuery, +public record SourceIdQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseQuery, ITagQuery { - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } @@ -18,44 +18,44 @@ public TSort GetSort(ILeaseSortBuilder builder) return builder.Combine ( builder.SourceId(true), - builder.EntityId(true), - builder.EntityVersion(true) + builder.StateId(true), + builder.StateVersion(true) ); } - public TFilter GetFilter(IMessageGroupFilterBuilder builder) + public TFilter GetFilter(IMessageDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } - public TSort GetSort(IMessageGroupSortBuilder builder) + public TSort GetSort(IMessageDataSortBuilder builder) { return builder.Combine ( - builder.SourceId(true) + builder.SourceId(true), + builder.StateId(true), + builder.StateVersion(true) ); } - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(IMessageFilterBuilder builder) + public TFilter GetFilter(ISourceDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } - public TSort GetSort(IMessageSortBuilder builder) + public TSort GetSort(ISourceDataSortBuilder builder) { return builder.Combine ( - builder.SourceId(true), - builder.EntityId(true), - builder.EntityVersion(true) + builder.SourceId(true) ); } - public TFilter GetFilter(ITagFilterBuilder builder) + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } @@ -65,8 +65,8 @@ public TSort GetSort(ITagSortBuilder builder) return builder.Combine ( builder.SourceId(true), - builder.EntityId(true), - builder.EntityVersion(true) + builder.StateId(true), + builder.StateVersion(true) ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/SourceTimeStampQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampQuery.cs similarity index 65% rename from test/EntityDb.Common.Tests/Implementations/Queries/SourceTimeStampQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampQuery.cs index 69146713..1116d3f8 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/SourceTimeStampQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampQuery.cs @@ -3,13 +3,13 @@ using EntityDb.Abstractions.Sources.Queries.SortBuilders; using EntityDb.Abstractions.ValueObjects; -namespace EntityDb.Common.Tests.Implementations.Queries; +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceTimeStampQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : IMessageGroupQuery, - IMessageQuery, +public record SourceTimeStampQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataQuery, + IMessageDataQuery, ILeaseQuery, ITagQuery { - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.And ( @@ -23,12 +23,12 @@ public TSort GetSort(ILeaseSortBuilder builder) return builder.Combine ( builder.SourceTimeStamp(true), - builder.EntityId(true), - builder.EntityVersion(true) + builder.StateId(true), + builder.StateVersion(true) ); } - public TFilter GetFilter(IMessageGroupFilterBuilder builder) + public TFilter GetFilter(IMessageDataFilterBuilder builder) { return builder.And ( @@ -37,19 +37,17 @@ public TFilter GetFilter(IMessageGroupFilterBuilder builder) ); } - public TSort GetSort(IMessageGroupSortBuilder builder) + public TSort GetSort(IMessageDataSortBuilder builder) { return builder.Combine ( - builder.SourceTimeStamp(true) + builder.SourceTimeStamp(true), + builder.StateId(true), + builder.StateVersion(true) ); } - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(IMessageFilterBuilder builder) + public TFilter GetFilter(ISourceDataFilterBuilder builder) { return builder.And ( @@ -58,17 +56,19 @@ public TFilter GetFilter(IMessageFilterBuilder builder) ); } - public TSort GetSort(IMessageSortBuilder builder) + public TSort GetSort(ISourceDataSortBuilder builder) { return builder.Combine ( - builder.SourceTimeStamp(true), - builder.EntityId(true), - builder.EntityVersion(true) + builder.SourceTimeStamp(true) ); } - public TFilter GetFilter(ITagFilterBuilder builder) + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.And ( @@ -82,8 +82,8 @@ public TSort GetSort(ITagSortBuilder builder) return builder.Combine ( builder.SourceTimeStamp(true), - builder.EntityId(true), - builder.EntityVersion(true) + builder.StateId(true), + builder.StateVersion(true) ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs new file mode 100644 index 00000000..d2c58e19 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs @@ -0,0 +1,66 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; + +public record StateQuery(Id StateId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, + ILeaseQuery, ITagQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.StateIdIn(StateId); + } + + public TSort GetSort(ILeaseSortBuilder builder) + { + return builder.Combine + ( + builder.StateId(true), + builder.StateVersion(true) + ); + } + + public TFilter GetFilter(IMessageDataFilterBuilder builder) + { + return builder.StateIdIn(StateId); + } + + public TSort GetSort(IMessageDataSortBuilder builder) + { + return builder.Combine + ( + builder.StateId(true), + builder.StateVersion(true) + ); + } + + public TFilter GetFilter(ISourceDataFilterBuilder builder) + { + return builder.AnyStateIdIn(StateId); + } + + public TSort GetSort(ISourceDataSortBuilder builder) + { + return builder.StateIds(true); + } + + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagDataFilterBuilder builder) + { + return builder.StateIdIn(StateId); + } + + public TSort GetSort(ITagSortBuilder builder) + { + return builder.Combine + ( + builder.StateId(true), + builder.StateVersion(true) + ); + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs new file mode 100644 index 00000000..fc3d058c --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs @@ -0,0 +1,56 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; + +public record StateVersionQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataQuery, + ILeaseQuery, ITagQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.And + ( + builder.StateVersionGte(Gte), + builder.StateVersionLte(Lte) + ); + } + + public TSort GetSort(ILeaseSortBuilder builder) + { + return builder.StateVersion(true); + } + + public TFilter GetFilter(IMessageDataFilterBuilder builder) + { + return builder.And + ( + builder.StateVersionGte(Gte), + builder.StateVersionLte(Lte) + ); + } + + public TSort GetSort(IMessageDataSortBuilder builder) + { + return builder.StateVersion(true); + } + + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagDataFilterBuilder builder) + { + return builder.And + ( + builder.StateVersionGte(Gte), + builder.StateVersionLte(Lte) + ); + } + + public TSort GetSort(ITagSortBuilder builder) + { + return builder.StateVersion(true); + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs similarity index 56% rename from test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs rename to test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs index 35717cfb..b3e2a7a9 100644 --- a/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs @@ -1,10 +1,10 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; -namespace EntityDb.Common.Tests.Implementations.Leases; +namespace EntityDb.Common.Tests.Implementations.States.Attributes; public record CountLease(ulong Number) : ILease { public string Scope => ""; public string Label => ""; public string Value => $"{Number}"; -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs similarity index 50% rename from test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs rename to test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs index f40d12ad..30df1708 100644 --- a/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Attributes; -namespace EntityDb.Common.Tests.Implementations.Tags; +namespace EntityDb.Common.Tests.Implementations.States.Attributes; public record CountTag(ulong Number) : ITag { public string Label => ""; public string Value => $"{Number}"; -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs new file mode 100644 index 00000000..8d7ffbd4 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Tests.Implementations.States; + +public interface IStateWithTestLogic : IState + where TState : class +{ + static abstract string MongoDbCollectionName { get; } + static abstract string RedisKeyNamespace { get; } + static abstract AsyncLocal?> ShouldRecordLogic { get; } + static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } +} diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index d50c168c..8f1025ea 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -1,11 +1,11 @@ -using System.Diagnostics.CodeAnalysis; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; using EntityDb.Common.Tests.Implementations.Seeders; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.States; using Shouldly; +using System.Diagnostics.CodeAnalysis; using Xunit; using Version = EntityDb.Abstractions.ValueObjects.Version; @@ -21,63 +21,64 @@ public ProjectionsTests(IServiceProvider startupServiceProvider, DatabaseContain } private async Task Generic_GivenEmptySourceRepository_WhenGettingProjection_ThenThrow( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, + StateRepositoryAdder projectionStateRepositoryAdder) where TProjection : IProjection { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); - projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + projectionStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var projectionRepository = await GetReadOnlyProjectionRepository(serviceScope, true); + await using var readOnlyRepository = await GetReadOnlyProjectionRepository(serviceScope, true); // ACT & ASSERT - await Should.ThrowAsync(() => - projectionRepository.GetSnapshot(default)); + await Should.ThrowAsync(() => + readOnlyRepository.Get(default)); } private async Task Generic_GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, - SnapshotAdder projectionSnapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - where TProjection : class, IProjection, ISnapshotWithTestLogic + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, + StateRepositoryAdder projectionStateRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic + where TProjection : class, IProjection, IStateWithTestLogic { // ARRANGE + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + projectionStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + await using var projectionRepository = await GetReadOnlyProjectionRepository(serviceScope, true); + const uint numberOfVersions = 5; const uint replaceAtVersionValue = 3; TProjection.ShouldRecordAsLatestLogic.Value = (projection, _) => - projection.Pointer.Version == new Version(replaceAtVersionValue); + projection.GetPointer().Version == new Version(replaceAtVersionValue); - var entityId = Id.NewId(); + var stateId = Id.NewId(); - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); - projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); - }); + entityRepository.Create(stateId); - await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); - await using var projectionRepository = await GetReadOnlyProjectionRepository(serviceScope, true); + entityRepository.Seed(stateId, replaceAtVersionValue); + + var firstSourceCommitted = await entityRepository.Commit(); + + entityRepository.Seed(stateId, numberOfVersions - replaceAtVersionValue); - entityRepository.Create(entityId); - - entityRepository.Seed(entityId, replaceAtVersionValue); - - var firstSourceCommitted = await entityRepository.Commit(Id.NewId()); - - entityRepository.Seed(entityId, numberOfVersions - replaceAtVersionValue); + var secondSourceCommitted = await entityRepository.Commit(); - var secondSourceCommitted = await entityRepository.Commit(Id.NewId()); - // ARRANGE ASSERTIONS numberOfVersions.ShouldBeGreaterThan(replaceAtVersionValue); @@ -85,41 +86,42 @@ private async Task firstSourceCommitted.ShouldBeTrue(); secondSourceCommitted.ShouldBeTrue(); - projectionRepository.SnapshotRepository.ShouldNotBeNull(); + projectionRepository.StateRepository.ShouldNotBeNull(); // ACT - var currentProjection = await projectionRepository.GetSnapshot(entityId); - var projectionSnapshot = await projectionRepository.SnapshotRepository.GetSnapshotOrDefault(entityId); + var currentProjection = await projectionRepository.Get(stateId); + var persistedProjection = await projectionRepository.StateRepository.Get(stateId); // ASSERT - currentProjection.Pointer.Version.Value.ShouldBe(numberOfVersions); - projectionSnapshot.ShouldNotBeNull(); - projectionSnapshot.Pointer.Version.Value.ShouldBe(replaceAtVersionValue); + currentProjection.GetPointer().Version.Value.ShouldBe(numberOfVersions); + persistedProjection.ShouldNotBeNull(); + persistedProjection.GetPointer().Version.Value.ShouldBe(replaceAtVersionValue); } [Theory] - [MemberData(nameof(AddSourcesEntitySnapshotsAndProjectionSnapshots))] - public Task GivenEmptySourceRepository_WhenGettingProjection_ThenThrow(SourcesAdder sourcesAdder, - SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + [MemberData(nameof(With_Source_EntityState_ProjectionState))] + public Task GivenEmptySourceRepository_WhenGettingProjection_ThenThrow(SourceRepositoryAdder sourceRepositoryAdder, + StateRepositoryAdder entityStateRepositoryAdder, StateRepositoryAdder projectionStateRepositoryAdder) { return RunGenericTestAsync ( - new[] { projectionSnapshotAdder.SnapshotType }, - new object?[] { sourcesAdder, entitySnapshotAdder, projectionSnapshotAdder } + new[] { projectionStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder, projectionStateRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddSourcesEntitySnapshotsAndProjectionSnapshots))] + [MemberData(nameof(With_Source_EntityState_ProjectionState))] public Task GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, + StateRepositoryAdder projectionStateRepositoryAdder) { return RunGenericTestAsync ( - new[] { entitySnapshotAdder.SnapshotType, projectionSnapshotAdder.SnapshotType }, - new object?[] { sourcesAdder, entitySnapshotAdder, projectionSnapshotAdder } + new[] { entityStateRepositoryAdder.StateType, projectionStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder, projectionStateRepositoryAdder } ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Properties/AssemblyInfo.cs b/test/EntityDb.Common.Tests/Properties/AssemblyInfo.cs index 67eb74b5..2baba57f 100644 --- a/test/EntityDb.Common.Tests/Properties/AssemblyInfo.cs +++ b/test/EntityDb.Common.Tests/Properties/AssemblyInfo.cs @@ -1,5 +1,7 @@ using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Xunit; [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] \ No newline at end of file +[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] +[assembly: InternalsVisibleTo("EntityDb.MongoDb.Tests")] diff --git a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs b/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs deleted file mode 100644 index e92c5f5f..00000000 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Exceptions; -using EntityDb.Common.Tests.Implementations.Snapshots; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; -using Moq; -using Shouldly; -using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; - -namespace EntityDb.Common.Tests.Snapshots; - -[Collection(nameof(DatabaseContainerCollection))] -[SuppressMessage("ReSharper", "UnusedMember.Local")] -public sealed class SnapshotTests : TestsBase -{ - public SnapshotTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : - base(startupServiceProvider, databaseContainerFixture) - { - } - - private async Task - Generic_GivenEmptySnapshotRepository_WhenSnapshotCommittedAndFetched_ThenCommittedMatchesFetched( - SnapshotAdder snapshotAdder) - where TSnapshot : class, ISnapshotWithTestLogic - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - var expectedSnapshot = TSnapshot.Construct(entityId).WithVersion(new Version(300)); - - var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetRequiredService>(); - - await using var snapshotRepository = await snapshotRepositoryFactory - .CreateRepository(TestSessionOptions.Write); - - // ACT - - var snapshotCommitted = await snapshotRepository.PutSnapshot(entityId, expectedSnapshot); - - var actualSnapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); - - // ASSERT - - snapshotCommitted.ShouldBeTrue(); - - actualSnapshot.ShouldBeEquivalentTo(expectedSnapshot); - } - - [Theory] - [MemberData(nameof(AddEntitySnapshots))] - [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenEmptySnapshotRepository_WhenSnapshotCommittedAndFetched_ThenCommittedMatchesFetched( - SnapshotAdder snapshotAdder) - { - return RunGenericTestAsync - ( - new[] { snapshotAdder.SnapshotType }, - new object?[] { snapshotAdder } - ); - } - - private async Task - Generic_GivenEmptySnapshotRepository_WhenCommittingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged< - TSnapshot>(SnapshotAdder snapshotAdder) - where TSnapshot : class, ISnapshotWithTestLogic - { - // ARRANGE - - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - snapshotAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.RemoveAll(typeof(ILoggerFactory)); - - serviceCollection.AddSingleton(loggerFactory); - }); - - var snapshot = TSnapshot.Construct(default).WithVersion(new Version(300)); - - await using var snapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); - - // ACT - - var inserted = await snapshotRepository.PutSnapshot(default, snapshot); - - // ASSERT - - inserted.ShouldBeFalse(); - - loggerVerifier.Invoke(Times.Once()); - } - - [Theory] - [MemberData(nameof(AddEntitySnapshots))] - [MemberData(nameof(AddProjectionSnapshots))] - public Task - GivenEmptySnapshotRepository_WhenCommittingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged( - SnapshotAdder snapshotAdder) - { - return RunGenericTestAsync - ( - new[] { snapshotAdder.SnapshotType }, - new object?[] { snapshotAdder } - ); - } - - private async Task - Generic_GivenCommittedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot( - SnapshotAdder snapshotAdder) - where TSnapshot : class, ISnapshotWithTestLogic - { - // ARRANGE - - Pointer latestPointer = Id.NewId(); - - var snapshot = TSnapshot.Construct(latestPointer).WithVersion(new Version(5000)); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - snapshotAdder.AddDependencies.Invoke(serviceCollection); - - TSnapshot.ShouldRecordAsLatestLogic.Value = (_, _) => true; - }); - - await using var writeSnapshotRepository = await GetWriteSnapshotRepository(serviceScope); - - var inserted = await writeSnapshotRepository.PutSnapshot(latestPointer, snapshot); - - // ARRANGE ASSERTIONS - - inserted.ShouldBeTrue(); - - // ACT - - var deleted = await writeSnapshotRepository.DeleteSnapshots(new[] { latestPointer }); - - var finalSnapshot = await writeSnapshotRepository.GetSnapshotOrDefault(latestPointer); - - // ASSERT - - deleted.ShouldBeTrue(); - - finalSnapshot.ShouldBe(default); - } - - [Theory] - [MemberData(nameof(AddEntitySnapshots))] - [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenCommittedSnapshotAsLatest_WhenSnapshotDeleted_ThenReturnNoSnapshot(SnapshotAdder snapshotAdder) - { - return RunGenericTestAsync - ( - new[] { snapshotAdder.SnapshotType }, - new object?[] { snapshotAdder } - ); - } - - private async Task Generic_GivenCommittedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot( - SnapshotAdder snapshotAdder) - where TSnapshot : class, ISnapshotWithTestLogic - { - // ARRANGE - - var entityId = Id.NewId(); - - var expectedSnapshot = TSnapshot.Construct(entityId).WithVersion(new Version(5000)); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - await using var writeSnapshotRepository = await GetWriteSnapshotRepository(serviceScope); - - await using var readOnlySnapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); - - await using var readOnlySecondaryPreferredSnapshotRepository = await GetReadOnlySnapshotRepository(serviceScope, secondaryPreferred: true); - - var inserted = await writeSnapshotRepository.PutSnapshot(entityId, expectedSnapshot); - - // ARRANGE ASSERTIONS - - inserted.ShouldBeTrue(); - - // ACT - - var readOnlySnapshot = await readOnlySnapshotRepository.GetSnapshotOrDefault(entityId); - - var readOnlySecondaryPreferredSnapshot = - await readOnlySecondaryPreferredSnapshotRepository.GetSnapshotOrDefault(entityId); - - // ASSERT - - readOnlySnapshot.ShouldBeEquivalentTo(expectedSnapshot); - readOnlySecondaryPreferredSnapshot.ShouldBeEquivalentTo(expectedSnapshot); - } - - [Theory] - [MemberData(nameof(AddEntitySnapshots))] - [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenCommittedSnapshot_WhenReadInVariousReadModes_ThenReturnSameSnapshot(SnapshotAdder snapshotAdder) - { - return RunGenericTestAsync - ( - new[] { snapshotAdder.SnapshotType }, - new object?[] { snapshotAdder } - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs b/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs deleted file mode 100644 index fab09c51..00000000 --- a/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Snapshots; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; -using Moq; -using Shouldly; -using Xunit; - -namespace EntityDb.Common.Tests.Snapshots; - -[SuppressMessage("ReSharper", "UnusedMember.Local")] -public class TryCatchSnapshotRepositoryTests : TestsBase -{ - public TryCatchSnapshotRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - - private async Task Generic_GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - - var snapshotRepositoryMock = new Mock>(MockBehavior.Strict); - - snapshotRepositoryMock - .Setup(repository => repository.GetSnapshotOrDefault(It.IsAny(), It.IsAny())) - .ThrowsAsync(new NotImplementedException()); - - snapshotRepositoryMock - .Setup(repository => - repository.PutSnapshot(It.IsAny(), It.IsAny(), It.IsAny())) - .ThrowsAsync(new NotImplementedException()); - - snapshotRepositoryMock - .Setup(repository => repository.DeleteSnapshots(It.IsAny(), It.IsAny())) - .ThrowsAsync(new NotImplementedException()); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.RemoveAll(typeof(ILoggerFactory)); - - serviceCollection.AddSingleton(loggerFactory); - }); - - var tryCatchSnapshotRepository = TryCatchSnapshotRepository - .Create(serviceScope.ServiceProvider, snapshotRepositoryMock.Object); - - // ACT - - var snapshot = await tryCatchSnapshotRepository.GetSnapshotOrDefault(default); - var inserted = await tryCatchSnapshotRepository.PutSnapshot(default, default!); - var deleted = await tryCatchSnapshotRepository.DeleteSnapshots(default!); - - // ASSERT - - snapshot.ShouldBe(default); - inserted.ShouldBeFalse(); - deleted.ShouldBeFalse(); - - loggerVerifier.Invoke(Times.Exactly(3)); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged(EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs b/test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs similarity index 85% rename from test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs rename to test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs index 4481a801..783da059 100644 --- a/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs +++ b/test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs @@ -6,7 +6,7 @@ using Shouldly; using Xunit; -namespace EntityDb.Common.Tests.Agents; +namespace EntityDb.Common.Tests.Sources.Agents; public abstract class AgentAccessorTestsBase : TestsBase where TStartup : IStartup, new() @@ -27,13 +27,16 @@ protected abstract void ConfigureActiveAgentAccessor(IServiceCollection serviceC protected abstract IEnumerable GetAgentAccessorOptions(); [Fact] - public void GivenBackingServiceInactive_WhenGettingAgent_ThenThrow() + public async Task GivenBackingServiceInactive_WhenGettingAgent_ThenThrow() { - if (!CanBeInactive) return; + if (!CanBeInactive) + { + return; + } // ARRANGE - using var serviceScope = CreateServiceScope(ConfigureInactiveAgentAccessor); + await using var serviceScope = CreateServiceScope(ConfigureInactiveAgentAccessor); var agentAccessor = serviceScope.ServiceProvider .GetRequiredService(); @@ -44,13 +47,13 @@ public void GivenBackingServiceInactive_WhenGettingAgent_ThenThrow() } [Fact] - public void GivenBackingServiceActive_WhenGettingAgent_ThenReturnAgent() + public async Task GivenBackingServiceActive_WhenGettingAgent_ThenReturnAgent() { foreach (var agentAccessorConfiguration in GetAgentAccessorOptions()) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); }); @@ -75,7 +78,7 @@ public async Task GivenBackingServiceActive_ThenCanGetTimestamp() { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); }); @@ -99,7 +102,7 @@ public async Task GivenBackingServiceActive_WhenGettingAgentSignature_ThenReturn { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); }); @@ -126,7 +129,7 @@ public async Task { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); @@ -158,10 +161,7 @@ public async Task { // ARRANGE - var expectedApplicationInfo = new Dictionary - { - ["UserId"] = Guid.NewGuid().ToString(), - }; + var expectedApplicationInfo = new Dictionary { ["UserId"] = Guid.NewGuid().ToString() }; var agentSignatureAugmenterMock = new Mock(MockBehavior.Strict); @@ -169,7 +169,7 @@ public async Task .Setup(x => x.GetApplicationInfo(It.IsAny())) .ReturnsAsync(expectedApplicationInfo); - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); @@ -191,4 +191,4 @@ public async Task actualApplicationInfo.ShouldBe(expectedApplicationInfo); } } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs b/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs similarity index 90% rename from test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs rename to test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs index a45ca800..4cef9c51 100644 --- a/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs +++ b/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs @@ -1,7 +1,7 @@ using EntityDb.Common.Sources.Agents; using Microsoft.Extensions.DependencyInjection; -namespace EntityDb.Common.Tests.Agents; +namespace EntityDb.Common.Tests.Sources.Agents; public class UnknownAgentAccessorTests : AgentAccessorTestsBase { @@ -23,10 +23,7 @@ protected override void ConfigureActiveAgentAccessor(IServiceCollection serviceC protected override IEnumerable GetAgentAccessorOptions() { - return new[] - { - new object(), - }; + return new[] { new object() }; } protected override Dictionary? GetApplicationInfo(object agentSignature) @@ -35,4 +32,4 @@ protected override IEnumerable GetAgentAccessorOptions() ? null : unknownAgentSignature.ApplicationInfo; } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs deleted file mode 100644 index e0cbbef9..00000000 --- a/test/EntityDb.Common.Tests/Sources/EntitySnapshotSourceSubscriberTests.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Tests.Implementations.Seeders; -using EntityDb.Common.Tests.Implementations.Snapshots; -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Xunit; - -namespace EntityDb.Common.Tests.Sources; - -[Collection(nameof(DatabaseContainerCollection))] -[SuppressMessage("ReSharper", "UnusedMember.Local")] -public class EntitySnapshotSourceSubscriberTests : TestsBase -{ - public EntitySnapshotSourceSubscriberTests(IServiceProvider startupServiceProvider, - DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) - { - } - - private async Task - Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotSourceSubscriber_ThenAlwaysWriteSnapshot< - TEntity>( - SourcesAdder sourcesAdder, SnapshotAdder snapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE - - TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => true; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - const uint numberOfVersions = 10; - - await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); - - await using var snapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); - - // ACT - - entityRepository.Create(entityId); - entityRepository.Seed(entityId, numberOfVersions); - - var committed = await entityRepository - .Commit(default); - - var snapshot = await snapshotRepository - .GetSnapshotOrDefault(entityId); - - // ASSERT - - committed.ShouldBeTrue(); - snapshot.ShouldNotBeNull(); - snapshot.Pointer.Version.Value.ShouldBe(numberOfVersions); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntitySnapshots))] - private Task - GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotSourceSubscriber_ThenAlwaysWriteSnapshot( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) - { - return RunGenericTestAsync - ( - new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { sourcesAdder, entitySnapshotAdder } - ); - } - - private async Task - Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotSourceSubscriber_ThenNeverWriteSnapshot< - TEntity>(SourcesAdder sourcesAdder, SnapshotAdder snapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE - - TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => false; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - sourcesAdder.AddDependencies.Invoke(serviceCollection); - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); - await using var snapshotRepository = await GetReadOnlySnapshotRepository(serviceScope); - - var entityId = Id.NewId(); - - // ACT - - entityRepository.Create(entityId); - entityRepository.Seed(entityId, 10); - - await entityRepository.Commit(default); - - var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); - - // ASSERT - - snapshot.ShouldBe(default); - } - - [Theory] - [MemberData(nameof(AddSourcesAndEntitySnapshots))] - private Task - GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotSourceSubscriber_ThenNeverWriteSnapshot( - SourcesAdder sourcesAdder, SnapshotAdder entitySnapshotAdder) - { - return RunGenericTestAsync - ( - new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { sourcesAdder, entitySnapshotAdder } - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs new file mode 100644 index 00000000..18b150f5 --- /dev/null +++ b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs @@ -0,0 +1,119 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Tests.Implementations.Seeders; +using EntityDb.Common.Tests.Implementations.States; +using Shouldly; +using System.Diagnostics.CodeAnalysis; +using Xunit; + +namespace EntityDb.Common.Tests.Sources; + +[Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class EntityStateSourceSubscriberTests : TestsBase +{ + public EntityStateSourceSubscriberTests(IServiceProvider startupServiceProvider, + DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) + { + } + + private async Task + Generic_GivenStateShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState< + TEntity>( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder stateRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + stateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + + await using var stateRepository = await GetReadOnlyStateRepository(serviceScope); + + TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => true; + + var entityId = Id.NewId(); + + const uint numberOfVersions = 10; + + // ACT + + entityRepository.Create(entityId); + entityRepository.Seed(entityId, numberOfVersions); + + var committed = await entityRepository.Commit(); + + var persistedEntity = await stateRepository.Get(entityId); + + // ASSERT + + committed.ShouldBeTrue(); + persistedEntity.ShouldNotBeNull(); + persistedEntity.GetPointer().Version.Value.ShouldBe(numberOfVersions); + } + + [Theory] + [MemberData(nameof(With_Source_EntityState))] + private Task + GivenStateShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder } + ); + } + + private async Task + Generic_GivenStateShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntityStateSourceSubscriber_ThenNeverPersistState< + TEntity>(SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder stateRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + stateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + await using var stateRepository = await GetReadOnlyStateRepository(serviceScope); + + TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => false; + + var entityId = Id.NewId(); + + // ACT + + entityRepository.Create(entityId); + entityRepository.Seed(entityId, 10); + + await entityRepository.Commit(); + + var persistedEntity = await stateRepository.Get(entityId); + + // ASSERT + + persistedEntity.ShouldBe(default); + } + + [Theory] + [MemberData(nameof(With_Source_EntityState))] + private Task + GivenStateShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntityStateSourceSubscriber_ThenNeverPersistState( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder } + ); + } +} diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 98a1c4dc..16653b7b 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -1,24 +1,22 @@ -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States.Attributes; using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Agents; -using EntityDb.Common.Sources.Attributes; using EntityDb.Common.Sources.Queries; using EntityDb.Common.Sources.Queries.Modified; using EntityDb.Common.Sources.Queries.Standard; -using EntityDb.Common.Tests.Implementations.Deltas; -using EntityDb.Common.Tests.Implementations.Leases; -using EntityDb.Common.Tests.Implementations.Queries; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; using EntityDb.Common.Tests.Implementations.Seeders; -using EntityDb.Common.Tests.Implementations.Tags; +using EntityDb.Common.Tests.Implementations.Sources.Queries; +using EntityDb.Common.Tests.Implementations.States.Attributes; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Shouldly; +using System.Diagnostics.CodeAnalysis; using Xunit; using Version = EntityDb.Abstractions.ValueObjects.Version; @@ -32,20 +30,20 @@ public SourceTests(IServiceProvider startupServiceProvider, DatabaseContainerFix : base(startupServiceProvider, databaseContainerFixture) { } - + private static async Task PutSources ( IServiceScope serviceScope, List sources ) { - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); foreach (var source in sources) { - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); } } @@ -71,6 +69,8 @@ bool secondaryPreferred { // ARRANGE + await using var readOnlyRepository = await GetReadOnlySourceRepository(serviceScope, secondaryPreferred); + var bufferModifier = NewModifiedQueryOptions(false, false, null, null); var negateModifier = NewModifiedQueryOptions(true, false, null, null); var reverseBufferModifier = NewModifiedQueryOptions(false, true, null, null); @@ -83,24 +83,18 @@ bool secondaryPreferred var reversedExpectedFalseResults = expectedFalseResults.Reverse().ToArray(); var expectedSkipTakeResults = expectedTrueResults.Skip(1).Take(1).ToArray(); - await using var sourceRepository = await serviceScope.ServiceProvider - .GetRequiredService() - .CreateRepository(secondaryPreferred - ? TestSessionOptions.ReadOnlySecondaryPreferred - : TestSessionOptions.ReadOnly); - // ACT var actualTrueResults = - await getActualResults.Invoke(sourceRepository, bufferModifier).ToArrayAsync(); + await getActualResults.Invoke(readOnlyRepository, bufferModifier).ToArrayAsync(); var actualFalseResults = - await getActualResults.Invoke(sourceRepository, negateModifier).ToArrayAsync(); + await getActualResults.Invoke(readOnlyRepository, negateModifier).ToArrayAsync(); var reversedActualTrueResults = - await getActualResults.Invoke(sourceRepository, reverseBufferModifier).ToArrayAsync(); + await getActualResults.Invoke(readOnlyRepository, reverseBufferModifier).ToArrayAsync(); var reversedActualFalseResults = - await getActualResults.Invoke(sourceRepository, reverseNegateModifier).ToArrayAsync(); + await getActualResults.Invoke(readOnlyRepository, reverseNegateModifier).ToArrayAsync(); var actualSkipTakeResults = - await getActualResults.Invoke(sourceRepository, bufferSubsetModifier).ToArrayAsync(); + await getActualResults.Invoke(readOnlyRepository, bufferSubsetModifier).ToArrayAsync(); // ASSERT @@ -114,7 +108,7 @@ bool secondaryPreferred private static async Task TestGetSourceIds ( IServiceScope serviceScope, - IMessageGroupQuery query, + ISourceDataQuery query, ExpectedObjects expectedObjects ) { @@ -141,7 +135,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetSourceIds ( IServiceScope serviceScope, - IMessageQuery query, + IMessageDataQuery query, ExpectedObjects expectedObjects ) { @@ -219,10 +213,10 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, } } - private static async Task TestGetEntityIds + private static async Task TestGetStateIds ( IServiceScope serviceScope, - IMessageGroupQuery query, + ISourceDataQuery query, ExpectedObjects expectedObjects ) { @@ -234,8 +228,8 @@ ExpectedObjects expectedObjects Id[] GetExpectedResults(bool invert) { return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) .ToArray(); } @@ -243,15 +237,15 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } - private static async Task TestGetEntityIds + private static async Task TestGetStateIds ( IServiceScope serviceScope, - IMessageQuery query, + IMessageDataQuery query, ExpectedObjects expectedObjects ) { @@ -263,8 +257,8 @@ ExpectedObjects expectedObjects Id[] GetExpectedResults(bool invert) { return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) .ToArray(); } @@ -272,12 +266,12 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } - private static async Task TestGetEntityIds + private static async Task TestGetStateIds ( IServiceScope serviceScope, ILeaseQuery query, @@ -292,8 +286,8 @@ ExpectedObjects expectedObjects Id[] GetExpectedResults(bool invert) { return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) .ToArray(); } @@ -301,12 +295,12 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } - private static async Task TestGetEntityIds + private static async Task TestGetStateIds ( IServiceScope serviceScope, ITagQuery query, @@ -321,8 +315,8 @@ ExpectedObjects expectedObjects Id[] GetExpectedResults(bool invert) { return (invert - ? expectedObjects.FalseEntityIds - : expectedObjects.TrueEntityIds) + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) .ToArray(); } @@ -330,7 +324,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateEntityPointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -338,7 +332,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetAgentSignatures ( IServiceScope serviceScope, - IMessageGroupQuery query, + ISourceDataQuery query, ExpectedObjects expectedObjects ) { @@ -365,7 +359,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetDeltas ( IServiceScope serviceScope, - IMessageQuery query, + IMessageDataQuery query, ExpectedObjects expectedObjects ) { @@ -447,29 +441,26 @@ private static Source CreateSource ( IEnumerable versionNumbers, Id? id = null, - Id? entityId = null, TimeStamp? timeStamp = null, - object? agentSignature = null, - IDeltaSeeder? deltaSeeder = null + Id? stateId = null, + object? agentSignature = null ) { - var nonNullableEntityId = entityId ?? Id.NewId(); + var nonNullableStateId = stateId ?? Id.NewId(); - deltaSeeder ??= new StoreNumberSeeder(); - return new Source { Id = id ?? Id.NewId(), TimeStamp = timeStamp ?? TimeStamp.UtcNow, AgentSignature = agentSignature ?? new UnknownAgentSignature(new Dictionary()), Messages = versionNumbers - .Select(versionNumber =>new Message + .Select(versionNumber => new Message { Id = Id.NewId(), - EntityPointer = nonNullableEntityId + new Version(versionNumber), - Delta = deltaSeeder.Create(versionNumber), + StatePointer = nonNullableStateId + new Version(versionNumber), + Delta = new StoreNumber(versionNumber), }) - .ToImmutableArray(), + .ToArray(), }; } @@ -482,23 +473,24 @@ private static Id[] GetSortedIds(int numberOfIds) .ToArray(); } - private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) + private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) where TOptions : class { const ulong countTo = 20UL; const ulong gte = 5UL; const ulong lte = 15UL; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); var sources = new List(); var expectedObjects = new ExpectedObjects(); var sourceIds = GetSortedIds((int)countTo); - var entityIds = GetSortedIds((int)countTo); + var stateIds = GetSortedIds((int)countTo); var agentSignature = new UnknownAgentSignature(new Dictionary()); @@ -507,13 +499,13 @@ private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenRe for (var i = 1UL; i <= countTo; i++) { var currentSourceId = sourceIds[i - 1]; - var currentEntityId = entityIds[i - 1]; + var currentStateId = stateIds[i - 1]; var leases = new[] { new CountLease(i) }; var tags = new[] { new CountTag(i) }; - expectedObjects.Add(i is >= gte and <= lte, currentSourceId, currentEntityId, agentSignature, deltas, + expectedObjects.Add(i is >= gte and <= lte, currentSourceId, currentStateId, agentSignature, deltas, leases, tags); var source = new Source @@ -522,15 +514,15 @@ private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenRe TimeStamp = TimeStamp.UtcNow, AgentSignature = agentSignature, Messages = deltas - .Select(delta =>new Message + .Select(delta => new Message { Id = Id.NewId(), - EntityPointer = currentEntityId, + StatePointer = currentStateId, Delta = delta, - AddLeases = leases.ToImmutableArray(), - AddTags = tags.ToImmutableArray(), + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), }) - .ToImmutableArray(), + .ToArray(), }; sources.Add(source); @@ -545,68 +537,66 @@ private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenRe await PutSources(serviceScope, sources); await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); await TestGetTags(serviceScope, query, expectedObjects); } - + [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenReadOnlyMode_WhenCommittingSource_ThenCannotWriteInReadOnlyModeExceptionIsLogged(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenReadOnlyMode_WhenCommittingSource_ThenReadOnlyWriteExceptionIsLogged( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - //var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - var logs = new List(); - - using var serviceScope = CreateServiceScope(serviceCollection => + + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.RemoveAll(typeof(ILoggerFactory)); serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); }); - await using var sourceRepository = await GetReadOnlySourceRepository(serviceScope); + await using var readOnlyRepository = await GetReadOnlySourceRepository(serviceScope); var source = CreateSource(new[] { 1ul }); // ACT - var committed = await sourceRepository.Commit(source); + var committed = await readOnlyRepository.Commit(source); // ASSERT committed.ShouldBeFalse(); - logs.Count(log => log.Exception is not null).ShouldBe(1); - - //loggerVerifier.Invoke(Times.Once()); + logs.Count(log => log.Exception is ReadOnlyWriteException).ShouldBe(1); } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); var firstSource = CreateSource(new[] { 1ul }); - var secondSource = CreateSource(new[] { 1ul }, id: firstSource.Id); + var secondSource = CreateSource(new[] { 1ul }, firstSource.Id); // ACT - var firstSourceCommitted = await sourceRepository.Commit(firstSource); - var secondSourceCommitted = await sourceRepository.Commit(secondSource); + var firstSourceCommitted = await writeRepository.Commit(firstSource); + var secondSourceCommitted = await writeRepository.Commit(secondSource); // ASSERT @@ -615,19 +605,20 @@ public async Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutRet } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - const int repeatCount = 2; - - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + const int repeatCount = 2; var source = CreateSource(new[] { 1ul }); @@ -636,7 +627,7 @@ public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(So Messages = Enumerable .Repeat(source.Messages, repeatCount) .SelectMany(messages => messages) - .ToImmutableArray(), + .ToArray(), }; // ARRANGE ASSERTIONS @@ -645,7 +636,7 @@ public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(So // ACT - var committed = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); // ASSERT @@ -653,83 +644,80 @@ public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse(So } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue(SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); var source = CreateSource(new[] { 0ul }); // ACT - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); // ASSERT - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - //var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); - var logs = new List(); - - using var serviceScope = CreateServiceScope(serviceCollection => + + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); serviceCollection.RemoveAll(typeof(ILoggerFactory)); serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - - var entityId = Id.NewId(); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var stateId = Id.NewId(); - var firstSource = CreateSource(new[] { 1ul }, entityId: entityId); - var secondSource = CreateSource(new[] { 1ul }, entityId: entityId); + var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); + var secondSource = CreateSource(new[] { 1ul }, stateId: stateId); // ACT - var firstSourceCommitted = await sourceRepository.Commit(firstSource); - var secondSourceCommitted = await sourceRepository.Commit(secondSource); + var firstSourceCommitted = await writeRepository.Commit(firstSource); + var secondSourceCommitted = await writeRepository.Commit(secondSource); // ASSERT firstSourceCommitted.ShouldBeTrue(); secondSourceCommitted.ShouldBeFalse(); - logs.Count(log => log.Exception is not null).ShouldBe(1); - - //loggerVerifier.Invoke(Times.Once()); + logs.Count(log => log.Exception is OptimisticConcurrencyException).ShouldBe(1); } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + var tag = TagSeeder.Create(); var source = new Source @@ -742,16 +730,16 @@ public async Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourcesAd new Message { Id = default, - EntityPointer = default, + StatePointer = default, Delta = new DoNothing(), - AddTags = new[] { tag, tag }.ToImmutableArray(), + AddTags = new[] { tag, tag }.ToArray(), }, - }.ToImmutableArray(), + }, }; // ACT - var committed = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); // ASSERT @@ -759,17 +747,18 @@ public async Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourcesAd } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); var lease = LeaseSeeder.Create(); @@ -782,17 +771,14 @@ public async Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse(Sour { new Message { - Id = default, - EntityPointer = default, - Delta = new DoNothing(), - AddLeases = new[] { lease, lease }.ToImmutableArray(), + Id = default, StatePointer = default, Delta = new DoNothing(), AddLeases = new[] { lease, lease }, }, - }.ToImmutableArray(), + }, }; // ACT - var committed = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); // ASSERT @@ -800,35 +786,36 @@ public async Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse(Sour } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); var expectedSourceId = Id.NewId(); - var expectedEntityId = Id.NewId(); - var expectedSourceTimeStamp = sourcesAdder.FixTimeStamp(TimeStamp.UtcNow); + var expectedStateId = Id.NewId(); + var expectedSourceTimeStamp = sourceRepositoryAdder.FixTimeStamp(TimeStamp.UtcNow); var expectedAgentSignature = new UnknownAgentSignature(new Dictionary()); var source = CreateSource ( new[] { 1ul }, - id: expectedSourceId, - timeStamp: expectedSourceTimeStamp, - entityId: expectedEntityId, - agentSignature: expectedAgentSignature + expectedSourceId, + expectedSourceTimeStamp, + expectedStateId, + expectedAgentSignature ); - - var committed = await sourceRepository.Commit(source); - var query = new EntityQuery(expectedEntityId); + var committed = await writeRepository.Commit(source); + + var query = new StateQuery(expectedStateId); // ARRANGE ASSERTIONS @@ -836,7 +823,7 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenRet // ACT - var annotatedAgentSignatures = await sourceRepository + var annotatedAgentSignatures = await writeRepository .EnumerateAnnotatedAgentSignatures(query) .ToArrayAsync(); @@ -846,41 +833,42 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenRet annotatedAgentSignatures[0].SourceId.ShouldBe(expectedSourceId); annotatedAgentSignatures[0].SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); - annotatedAgentSignatures[0].EntityPointers.Length.ShouldBe(1); - annotatedAgentSignatures[0].EntityPointers[0].Id.ShouldBe(expectedEntityId); + annotatedAgentSignatures[0].StatePointers.Length.ShouldBe(1); + annotatedAgentSignatures[0].StatePointers[0].Id.ShouldBe(expectedStateId); annotatedAgentSignatures[0].Data.ShouldBeEquivalentTo(expectedAgentSignature); } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE ulong[] numbers = { 1, 2, 3, 4, 5 }; - - using var serviceScope = CreateServiceScope(serviceCollection => + + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); var expectedSourceId = Id.NewId(); - var expectedEntityId = Id.NewId(); - var expectedSourceTimeStamp = sourcesAdder.FixTimeStamp(TimeStamp.UtcNow); + var expectedStateId = Id.NewId(); + var expectedSourceTimeStamp = sourceRepositoryAdder.FixTimeStamp(TimeStamp.UtcNow); var source = CreateSource ( numbers, - id: expectedSourceId, - timeStamp: expectedSourceTimeStamp, - entityId: expectedEntityId + expectedSourceId, + expectedSourceTimeStamp, + expectedStateId ); - var committed = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); - var query = new GetDeltasQuery(expectedEntityId, default); + var query = new GetDeltasQuery(expectedStateId, default); // ARRANGE ASSERTIONS @@ -888,7 +876,7 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnot // ACT - var annotatedDeltas = await sourceRepository.EnumerateAnnotatedDeltas(query).ToArrayAsync(); + var annotatedDeltas = await writeRepository.EnumerateAnnotatedDeltas(query).ToArrayAsync(); // ASSERT @@ -897,11 +885,11 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnot foreach (var number in numbers) { var annotatedDelta = annotatedDeltas[Convert.ToInt32(number) - 1]; - + annotatedDelta.SourceId.ShouldBe(expectedSourceId); annotatedDelta.SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); - annotatedDelta.EntityPointer.Id.ShouldBe(expectedEntityId); - annotatedDelta.EntityPointer.Version.ShouldBe(new Version(number)); + annotatedDelta.StatePointer.Id.ShouldBe(expectedStateId); + annotatedDelta.StatePointer.Version.ShouldBe(new Version(number)); annotatedDelta.Data .ShouldBeAssignableTo() @@ -912,22 +900,23 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnot } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEntityHasNoTags(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenMessageWithTagsCommitted_WhenRemovingAllTags_ThenSourceHasNoTags( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - - var entityId = Id.NewId(); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); - var tag = new Tag("Foo", "Bar"); - var tags = new[] { tag }.ToImmutableArray(); + var stateId = Id.NewId(); + + var tag = TagSeeder.Create(); + var tags = new[] { tag }; var initialSource = new Source { @@ -938,16 +927,13 @@ public async Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEnti { new Message { - Id = Id.NewId(), - Delta = new DoNothing(), - EntityPointer = entityId, - AddTags = tags, + Id = Id.NewId(), Delta = new DoNothing(), StatePointer = stateId, AddTags = tags, }, - }.ToImmutableArray(), + }, }; - var initialSourceCommitted = await sourceRepository.Commit(initialSource); - + var initialSourceCommitted = await writeRepository.Commit(initialSource); + var finalSource = new Source { Id = Id.NewId(), @@ -957,15 +943,12 @@ public async Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEnti { new Message { - Id = Id.NewId(), - Delta = new DoNothing(), - EntityPointer = entityId, - DeleteTags = tags, + Id = Id.NewId(), Delta = new DoNothing(), StatePointer = stateId, DeleteTags = tags, }, - }.ToImmutableArray(), + }, }; - var tagQuery = new DeleteTagsQuery(entityId, tags); + var tagQuery = new DeleteTagsQuery(stateId, tags); // ARRANGE ASSERTIONS @@ -973,13 +956,13 @@ public async Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEnti // ACT - var initialTags = await sourceRepository + var initialTags = await writeRepository .EnumerateTags(tagQuery) .ToArrayAsync(); - var finalSourceCommitted = await sourceRepository.Commit(finalSource); + var finalSourceCommitted = await writeRepository.Commit(finalSource); - var finalTags = await sourceRepository + var finalTags = await writeRepository .EnumerateTags(tagQuery) .ToArrayAsync(); @@ -992,20 +975,21 @@ public async Task GivenEntityCommittedWithTags_WhenRemovingAllTags_ThenFinalEnti } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinalEntityHasNoLeases(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenMessageWithLeasesCommitted_WhenRemovingAllLeases_ThenSourceHasNoLeases( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - - var lease = new Lease("Foo", "Bar", "Baz"); - var leases = new[] { lease }.ToImmutableArray(); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var lease = LeaseSeeder.Create(); + var leases = new[] { lease }; var initialSource = new Source { @@ -1016,16 +1000,13 @@ public async Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinal { new Message { - Id = Id.NewId(), - Delta = new DoNothing(), - EntityPointer = default, - AddLeases = leases, - } - }.ToImmutableArray(), + Id = Id.NewId(), Delta = new DoNothing(), StatePointer = default, AddLeases = leases, + }, + }, }; - - var initialSourceCommitted = await sourceRepository.Commit(initialSource); - + + var initialSourceCommitted = await writeRepository.Commit(initialSource); + var finalSource = new Source { Id = Id.NewId(), @@ -1035,12 +1016,9 @@ public async Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinal { new Message { - Id = Id.NewId(), - Delta = new DoNothing(), - EntityPointer = default, - DeleteLeases = leases, + Id = Id.NewId(), Delta = new DoNothing(), StatePointer = default, DeleteLeases = leases, }, - }.ToImmutableArray(), + }, }; var leaseQuery = new DeleteLeasesQuery(leases); @@ -1051,13 +1029,13 @@ public async Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinal // ACT - var initialLeases = await sourceRepository + var initialLeases = await writeRepository .EnumerateLeases(leaseQuery) .ToArrayAsync(); - - var finalSourceCommitted = await sourceRepository.Commit(finalSource); - var finalLeases = await sourceRepository + var finalSourceCommitted = await writeRepository.Commit(finalSource); + + var finalLeases = await writeRepository .EnumerateLeases(leaseQuery) .ToArrayAsync(); @@ -1069,65 +1047,67 @@ public async Task GivenEntityCommittedWithLeases_WhenRemovingAllLeases_ThenFinal } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenSourceCreatesEntity_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenMessageCommitted_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); + await using var writeRepository = await GetWriteSourceRepository(serviceScope); var expectedDelta = new StoreNumber(1); var source = CreateSource(new[] { 1ul }); - var versionOneQuery = new EntityVersionQuery(new Version(1), new Version(1)); + var versionOneQuery = new StateVersionQuery(new Version(1), new Version(1)); // ACT - var sourceCommitted = await sourceRepository.Commit(source); + var committed = await writeRepository.Commit(source); - var newDeltas = await sourceRepository.EnumerateDeltas(versionOneQuery).ToArrayAsync(); + var newDeltas = await writeRepository.EnumerateDeltas(versionOneQuery).ToArrayAsync(); // ASSERT - sourceCommitted.ShouldBeTrue(); + committed.ShouldBeTrue(); newDeltas.Length.ShouldBe(1); newDeltas[0].ShouldBeEquivalentTo(expectedDelta); } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionTwo_ThenReturnExpectedDelta(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenTwoMessagesCommitted_WhenQueryingForVersionTwo_ThenReturnExpectedDelta( + SourceRepositoryAdder sourceRepositoryAdder) { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); - - await using var sourceRepository = await GetWriteSourceRepository(serviceScope); - + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + var expectedDelta = new StoreNumber(2); - var entityId = Id.NewId(); - var firstSource = CreateSource(new[] { 1ul }, entityId: entityId); - var secondSource = CreateSource(new[] { 2ul }, entityId: entityId); + var stateId = Id.NewId(); + var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); + var secondSource = CreateSource(new[] { 2ul }, stateId: stateId); - var versionTwoQuery = new EntityVersionQuery(new Version(2), new Version(2)); + var versionTwoQuery = new StateVersionQuery(new Version(2), new Version(2)); // ACT - var firstSourceCommitted = await sourceRepository.Commit(firstSource); + var firstSourceCommitted = await writeRepository.Commit(firstSource); - var secondSourceCommitted = await sourceRepository.Commit(secondSource); + var secondSourceCommitted = await writeRepository.Commit(secondSource); - var newDeltas = await sourceRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); + var newDeltas = await writeRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); // ASSERT @@ -1138,16 +1118,17 @@ public async Task GivenSourceAppendsEntityWithOneVersion_WhenQueryingForVersionT } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) { const ulong timeSpanInMinutes = 60UL; const ulong gteInMinutes = 20UL; const ulong lteInMinutes = 30UL; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); var originTimeStamp = TimeStamp.UnixEpoch; @@ -1156,7 +1137,7 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then var expectedObjects = new ExpectedObjects(); var sourceIds = GetSortedIds((int)timeSpanInMinutes); - var entityIds = GetSortedIds((int)timeSpanInMinutes); + var stateIds = GetSortedIds((int)timeSpanInMinutes); TimeStamp? gte = null; TimeStamp? lte = null; @@ -1164,7 +1145,7 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then for (var i = 1UL; i <= timeSpanInMinutes; i++) { var currentSourceId = sourceIds[i - 1]; - var currentEntityId = entityIds[i - 1]; + var currentStateId = stateIds[i - 1]; var currentSourceTimeStamp = new TimeStamp(originTimeStamp.Value.AddMinutes(i)); var agentSignature = new UnknownAgentSignature(new Dictionary()); @@ -1173,7 +1154,7 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then var leases = new[] { new CountLease(i) }; var tags = new[] { new CountTag(i) }; - expectedObjects.Add(i is >= gteInMinutes and <= lteInMinutes, currentSourceId, currentEntityId, + expectedObjects.Add(i is >= gteInMinutes and <= lteInMinutes, currentSourceId, currentStateId, agentSignature, deltas, leases, tags); switch (i) @@ -1193,15 +1174,15 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then TimeStamp = currentSourceTimeStamp, AgentSignature = agentSignature, Messages = deltas - .Select(delta =>new Message + .Select(delta => new Message { Id = Id.NewId(), - EntityPointer = currentEntityId, + StatePointer = currentStateId, Delta = delta, - AddLeases = leases.ToImmutableArray(), - AddTags = tags.ToImmutableArray(), + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), }) - .ToImmutableArray(), + .ToArray(), }; sources.Add(source); @@ -1213,14 +1194,14 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then var query = new SourceTimeStampQuery(gte.Value, lte.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IMessageGroupQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IMessageQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1228,16 +1209,16 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then } [Theory] - [MemberData(nameof(AddSource))] + [MemberData(nameof(With_Source))] public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( - SourcesAdder sourcesAdder) + SourceRepositoryAdder sourceRepositoryAdder) { const ulong numberOfSourceIds = 10UL; const ulong whichSourceId = 5UL; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); var sources = new List(); @@ -1246,24 +1227,27 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnE Id? sourceId = null; var sourceIds = GetSortedIds((int)numberOfSourceIds); - var entityIds = GetSortedIds((int)numberOfSourceIds); + var stateIds = GetSortedIds((int)numberOfSourceIds); var agentSignature = new UnknownAgentSignature(new Dictionary()); for (var i = 1UL; i <= numberOfSourceIds; i++) { var currentSourceId = sourceIds[i - 1]; - var currentEntityId = entityIds[i - 1]; + var currentStateId = stateIds[i - 1]; var deltas = new object[] { new StoreNumber(i) }; var leases = new[] { new CountLease(i) }; var tags = new[] { new CountTag(i) }; - expectedObjects.Add(i == whichSourceId, currentSourceId, currentEntityId, agentSignature, + expectedObjects.Add(i == whichSourceId, currentSourceId, currentStateId, agentSignature, deltas, leases, tags); - if (i == whichSourceId) sourceId = currentSourceId; + if (i == whichSourceId) + { + sourceId = currentSourceId; + } var source = new Source { @@ -1271,15 +1255,15 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnE TimeStamp = TimeStamp.UtcNow, AgentSignature = agentSignature, Messages = deltas - .Select(delta =>new Message + .Select(delta => new Message { Id = Id.NewId(), - EntityPointer = currentEntityId, + StatePointer = currentStateId, Delta = delta, - AddLeases = leases.ToImmutableArray(), - AddTags = tags.ToImmutableArray(), + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), }) - .ToImmutableArray(), + .ToArray(), }; sources.Add(source); @@ -1290,14 +1274,14 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnE var query = new SourceIdQuery(sourceId.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IMessageGroupQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IMessageQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1305,31 +1289,32 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnE } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateId_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) { - const ulong numberOfEntityIds = 10UL; - const ulong whichEntityId = 5UL; + const ulong numberOfStateIds = 10UL; + const ulong whichStateId = 5UL; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); var sources = new List(); var expectedObjects = new ExpectedObjects(); - Id? entityId = null; + Id? stateId = null; - var sourceIds = GetSortedIds((int)numberOfEntityIds); - var entityIds = GetSortedIds((int)numberOfEntityIds); + var sourceIds = GetSortedIds((int)numberOfStateIds); + var stateIds = GetSortedIds((int)numberOfStateIds); var agentSignature = new UnknownAgentSignature(new Dictionary()); - for (var i = 1UL; i <= numberOfEntityIds; i++) + for (var i = 1UL; i <= numberOfStateIds; i++) { var currentSourceId = sourceIds[i - 1]; - var currentEntityId = entityIds[i - 1]; + var currentStateId = stateIds[i - 1]; var deltas = new object[] { new StoreNumber(i) }; @@ -1337,44 +1322,47 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnE var tags = new[] { new CountTag(i) }; - expectedObjects.Add(i == whichEntityId, currentSourceId, currentEntityId, agentSignature, deltas, + expectedObjects.Add(i == whichStateId, currentSourceId, currentStateId, agentSignature, deltas, leases, tags); - if (i == whichEntityId) entityId = currentEntityId; - + if (i == whichStateId) + { + stateId = currentStateId; + } + var source = new Source { Id = currentSourceId, TimeStamp = TimeStamp.UtcNow, AgentSignature = agentSignature, Messages = deltas - .Select(delta =>new Message + .Select(delta => new Message { Id = Id.NewId(), - EntityPointer = currentEntityId, + StatePointer = currentStateId, Delta = delta, - AddLeases = leases.ToImmutableArray(), - AddTags = tags.ToImmutableArray(), + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), }) - .ToImmutableArray(), + .ToArray(), }; sources.Add(source); } - entityId.ShouldNotBeNull(); + stateId.ShouldNotBeNull(); - var query = new EntityQuery(entityId.Value); + var query = new StateQuery(stateId.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as IMessageGroupQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IMessageGroupQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as IMessageQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetEntityIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1382,16 +1370,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityId_ThenReturnE } [Theory] - [MemberData(nameof(AddSource))] - public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateVersion_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) { const ulong numberOfVersions = 20; const ulong gte = 5UL; const ulong lte = 15UL; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - sourcesAdder.AddDependencies.Invoke(serviceCollection); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); var counts = new List(); @@ -1409,28 +1398,28 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenRe expectedObjects.Add(i is >= gte and <= lte, default, default, default!, new[] { delta }, leases, tags); - + messages.Add(new Message { Id = Id.NewId(), - EntityPointer = default, + StatePointer = default, Delta = delta, - AddLeases = leases.ToImmutableArray(), - AddTags = tags.ToImmutableArray(), + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), }); } - + var source = new Source { Id = Id.NewId(), TimeStamp = TimeStamp.UtcNow, AgentSignature = new UnknownAgentSignature(new Dictionary()), - Messages = messages.ToImmutableArray(), + Messages = messages.ToArray(), }; - + var sources = new List { source }; - var query = new EntityVersionQuery(new Version(gte), new Version(lte)); + var query = new StateVersionQuery(new Version(gte), new Version(lte)); await PutSources(serviceScope, sources); await TestGetDeltas(serviceScope, query, expectedObjects); @@ -1439,13 +1428,14 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByEntityVersion_ThenRe } [Theory] - [MemberData(nameof(AddSource))] - public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects(SourcesAdder sourcesAdder) + [MemberData(nameof(With_Source))] + public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) { return RunGenericTestAsync ( - new[] { sourcesAdder.QueryOptionsType }, - new object?[] { sourcesAdder } + new[] { sourceRepositoryAdder.QueryOptionsType }, + new object?[] { sourceRepositoryAdder } ); } @@ -1453,23 +1443,23 @@ private class ExpectedObjects { public readonly List FalseAgentSignatures = new(); public readonly List FalseDeltas = new(); - public readonly List FalseEntityIds = new(); public readonly List FalseLeases = new(); public readonly List FalseSourceIds = new(); + public readonly List FalseStateIds = new(); public readonly List FalseTags = new(); public readonly List TrueAgentSignatures = new(); public readonly List TrueDeltas = new(); - public readonly List TrueEntityIds = new(); public readonly List TrueLeases = new(); public readonly List TrueSourceIds = new(); + public readonly List TrueStateIds = new(); public readonly List TrueTags = new(); public void Add ( bool condition, Id sourceId, - Id entityId, + Id stateId, object agentSignature, IEnumerable deltas, IEnumerable leases, @@ -1479,7 +1469,7 @@ IEnumerable tags if (condition) { TrueSourceIds.Add(sourceId); - TrueEntityIds.Add(entityId); + TrueStateIds.Add(stateId); TrueAgentSignatures.Add(agentSignature); TrueDeltas.AddRange(deltas); TrueLeases.AddRange(leases); @@ -1488,7 +1478,7 @@ IEnumerable tags else { FalseSourceIds.Add(sourceId); - FalseEntityIds.Add(entityId); + FalseStateIds.Add(stateId); FalseAgentSignatures.Add(agentSignature); FalseDeltas.AddRange(deltas); FalseLeases.AddRange(leases); @@ -1496,4 +1486,4 @@ IEnumerable tags } } } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs index ba2393a9..59328f04 100644 --- a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -19,18 +19,20 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti { // ARRANGE - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + var logs = new List(); + + var loggerFactory = GetMockedLoggerFactory(logs); var sourceRepositoryMock = new Mock(MockBehavior.Strict); sourceRepositoryMock .Setup(repository => - repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock @@ -43,31 +45,32 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti sourceRepositoryMock .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateEntityPointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) + repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Setup(repository => + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock @@ -80,13 +83,13 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti sourceRepositoryMock .Setup(repository => - repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), + repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock @@ -99,22 +102,22 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti // ACT - var sourceIdsFromMessageGroupQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageGroupQuery)!).ToArrayAsync(); - var sourceIdsFromMessageQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageQuery)!).ToArrayAsync(); + var sourceIdsFromSourceDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ISourceDataQuery)!).ToArrayAsync(); + var sourceIdsFromMessageDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageDataQuery)!).ToArrayAsync(); var sourceIdsFromLeaseQuery = await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseQuery)!).ToArrayAsync(); var sourceIdsFromTagQuery = await tryCatchSourceRepository.EnumerateSourceIds(default(ITagQuery)!).ToArrayAsync(); - var entityPointersFromMessageGroupQuery = - await tryCatchSourceRepository.EnumerateEntityPointers(default(IMessageGroupQuery)!).ToArrayAsync(); - var entityPointersFromMessageQuery = - await tryCatchSourceRepository.EnumerateEntityPointers(default(IMessageQuery)!).ToArrayAsync(); - var entityPointersFromLeaseQuery = - await tryCatchSourceRepository.EnumerateEntityPointers(default(ILeaseQuery)!).ToArrayAsync(); - var entityPointersFromTagQuery = - await tryCatchSourceRepository.EnumerateEntityPointers(default(ITagQuery)!).ToArrayAsync(); + var statePointersFromSourceDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ISourceDataQuery)!).ToArrayAsync(); + var statePointersFromMessageDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(IMessageDataQuery)!).ToArrayAsync(); + var statePointersFromLeaseQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ILeaseQuery)!).ToArrayAsync(); + var statePointersFromTagQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ITagQuery)!).ToArrayAsync(); var agentSignatures = await tryCatchSourceRepository.EnumerateAgentSignatures(default!).ToArrayAsync(); var deltas = @@ -130,20 +133,21 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti // ASSERT - sourceIdsFromMessageGroupQuery.ShouldBeEmpty(); - sourceIdsFromMessageQuery.ShouldBeEmpty(); + sourceIdsFromSourceDataQuery.ShouldBeEmpty(); + sourceIdsFromMessageDataQuery.ShouldBeEmpty(); sourceIdsFromLeaseQuery.ShouldBeEmpty(); sourceIdsFromTagQuery.ShouldBeEmpty(); - entityPointersFromMessageGroupQuery.ShouldBeEmpty(); - entityPointersFromMessageQuery.ShouldBeEmpty(); - entityPointersFromLeaseQuery.ShouldBeEmpty(); - entityPointersFromTagQuery.ShouldBeEmpty(); + statePointersFromSourceDataQuery.ShouldBeEmpty(); + statePointersFromMessageDataQuery.ShouldBeEmpty(); + statePointersFromLeaseQuery.ShouldBeEmpty(); + statePointersFromTagQuery.ShouldBeEmpty(); agentSignatures.ShouldBeEmpty(); deltas.ShouldBeEmpty(); leases.ShouldBeEmpty(); tags.ShouldBeEmpty(); annotatedDeltas.ShouldBeEmpty(); committed.ShouldBeFalse(); - loggerVerifier.Invoke(Times.Exactly(14)); + + logs.Count(log => log.Exception is NotImplementedException).ShouldBe(14); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Startup.cs b/test/EntityDb.Common.Tests/Startup.cs index 52e64f1f..2d95e9ca 100644 --- a/test/EntityDb.Common.Tests/Startup.cs +++ b/test/EntityDb.Common.Tests/Startup.cs @@ -2,4 +2,4 @@ public class Startup : StartupBase { -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/StartupBase.cs b/test/EntityDb.Common.Tests/StartupBase.cs index f3229dfb..fe728df9 100644 --- a/test/EntityDb.Common.Tests/StartupBase.cs +++ b/test/EntityDb.Common.Tests/StartupBase.cs @@ -24,4 +24,4 @@ public virtual void AddServices(IServiceCollection serviceCollection) serviceCollection.AddSourceProcessorQueue(true); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/States/StateTests.cs b/test/EntityDb.Common.Tests/States/StateTests.cs new file mode 100644 index 00000000..2cf1c5b4 --- /dev/null +++ b/test/EntityDb.Common.Tests/States/StateTests.cs @@ -0,0 +1,216 @@ +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Tests.Implementations.States; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Shouldly; +using System.Diagnostics.CodeAnalysis; +using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.States; + +[Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class StateTests : TestsBase +{ + public StateTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : + base(startupServiceProvider, databaseContainerFixture) + { + } + + private async Task + Generic_GivenEmptyStateRepository_WhenSnapshotPersistedAndFetched_ThenPersistedMatchesFetched( + StateRepositoryAdder stateRepositoryAdder) + where TState : class, IState + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + stateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteStateRepository(serviceScope); + + var stateId = Id.NewId(); + var expectedState = TState.Construct(stateId + new Version(300)); + + // ACT + + var persisted = await writeRepository.Put(stateId, expectedState); + + var actualState = await writeRepository.Get(stateId); + + // ASSERT + + persisted.ShouldBeTrue(); + + actualState.ShouldBeEquivalentTo(expectedState); + } + + [Theory] + [MemberData(nameof(With_EntityState))] + [MemberData(nameof(With_ProjectionState))] + public Task GivenEmptyStateRepository_WhenSnapshotPersistedAndFetched_ThenPersistedMatchesFetched( + StateRepositoryAdder stateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { stateRepositoryAdder.StateType }, + new object?[] { stateRepositoryAdder } + ); + } + + private async Task + Generic_GivenEmptyStateRepository_WhenPersistingStateInReadOnlyMode_ThenReadOnlyWriteExceptionIsLogged< + TState>(StateRepositoryAdder stateRepositoryAdder) + where TState : class, IState + { + // ARRANGE + + var logs = new List(); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + stateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.RemoveAll(typeof(ILoggerFactory)); + + serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); + }); + + await using var readOnlyRepository = await GetReadOnlyStateRepository(serviceScope); + + var stateSnapshot = TState.Construct(Id.NewId() + new Version(300)); + + // ACT + + var persisted = await readOnlyRepository.Put(default, stateSnapshot); + + // ASSERT + + persisted.ShouldBeFalse(); + + logs.Count(log => log.Exception is ReadOnlyWriteException).ShouldBe(1); + } + + [Theory] + [MemberData(nameof(With_EntityState))] + [MemberData(nameof(With_ProjectionState))] + public Task + GivenEmptyStateRepository_WhenPersistingStateInReadOnlyMode_ThenReadOnlyWriteExceptionIsLogged( + StateRepositoryAdder stateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { stateRepositoryAdder.StateType }, + new object?[] { stateRepositoryAdder } + ); + } + + private async Task + Generic_GivenPersistedStateAsLatest_WhenStateDeleted_ThenReturnNoStates( + StateRepositoryAdder stateRepositoryAdder) + where TState : class, IStateWithTestLogic + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + stateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteStateRepository(serviceScope); + + TState.ShouldRecordAsLatestLogic.Value = (_, _) => true; + + Pointer latestPointer = Id.NewId(); + + var stateSnapshot = TState.Construct(latestPointer.Id + new Version(5000)); + + var persisted = await writeRepository.Put(latestPointer, stateSnapshot); + + // ARRANGE ASSERTIONS + + persisted.ShouldBeTrue(); + + // ACT + + var deleted = await writeRepository.Delete(new[] { latestPointer }); + + var finalSnapshot = await writeRepository.Get(latestPointer); + + // ASSERT + + deleted.ShouldBeTrue(); + + finalSnapshot.ShouldBe(default); + } + + [Theory] + [MemberData(nameof(With_EntityState))] + [MemberData(nameof(With_ProjectionState))] + public Task GivenPersistedStateAsLatest_WhenStateDeleted_ThenReturnNoStates( + StateRepositoryAdder stateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { stateRepositoryAdder.StateType }, + new object?[] { stateRepositoryAdder } + ); + } + + private async Task Generic_GivenPersistedState_WhenReadInVariousReadModes_ThenReturnSameState( + StateRepositoryAdder stateRepositoryAdder) + where TState : class, IState + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + stateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteStateRepository(serviceScope); + await using var readOnlyRepository = await GetReadOnlyStateRepository(serviceScope); + await using var readOnlySecondaryRepository = await GetReadOnlyStateRepository(serviceScope, true); + + var stateId = Id.NewId(); + + var expectedSnapshot = TState.Construct(stateId + new Version(5000)); + + var persisted = await writeRepository.Put(stateId, expectedSnapshot); + + // ARRANGE ASSERTIONS + + persisted.ShouldBeTrue(); + + // ACT + + var readOnlySnapshot = await readOnlyRepository.Get(stateId); + + var readOnlySecondaryPreferredSnapshot = + await readOnlySecondaryRepository.Get(stateId); + + // ASSERT + + readOnlySnapshot.ShouldBeEquivalentTo(expectedSnapshot); + readOnlySecondaryPreferredSnapshot.ShouldBeEquivalentTo(expectedSnapshot); + } + + [Theory] + [MemberData(nameof(With_EntityState))] + [MemberData(nameof(With_ProjectionState))] + public Task GivenPersistedState_WhenReadInVariousReadModes_ThenReturnSameState( + StateRepositoryAdder stateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { stateRepositoryAdder.StateType }, + new object?[] { stateRepositoryAdder } + ); + } +} diff --git a/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs new file mode 100644 index 00000000..828ab058 --- /dev/null +++ b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs @@ -0,0 +1,85 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.States; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Moq; +using Shouldly; +using System.Diagnostics.CodeAnalysis; +using Xunit; + +namespace EntityDb.Common.Tests.States; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class TryCatchStateRepositoryTests : TestsBase +{ + public TryCatchStateRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + private async Task Generic_GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged( + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : IEntity + { + // ARRANGE + + var logs = new List(); + + var loggerFactory = GetMockedLoggerFactory(logs); + + var stateRepositoryMock = new Mock>(MockBehavior.Strict); + + stateRepositoryMock + .Setup(repository => repository.Get(It.IsAny(), It.IsAny())) + .ThrowsAsync(new NotImplementedException()); + + stateRepositoryMock + .Setup(repository => + repository.Put(It.IsAny(), It.IsAny(), It.IsAny())) + .ThrowsAsync(new NotImplementedException()); + + stateRepositoryMock + .Setup(repository => repository.Delete(It.IsAny(), It.IsAny())) + .ThrowsAsync(new NotImplementedException()); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.RemoveAll(typeof(ILoggerFactory)); + + serviceCollection.AddSingleton(loggerFactory); + }); + + var tryCatchStateRepository = TryCatchStateRepository + .Create(serviceScope.ServiceProvider, stateRepositoryMock.Object); + + // ACT + + var state = await tryCatchStateRepository.Get(default); + var persisted = await tryCatchStateRepository.Put(default, default!); + var deleted = await tryCatchStateRepository.Delete(default!); + + // ASSERT + + state.ShouldBe(default); + persisted.ShouldBeFalse(); + deleted.ShouldBeFalse(); + + logs.Count(log => log.Exception is NotImplementedException).ShouldBe(3); + } + + [Theory] + [MemberData(nameof(With_Entity))] + public Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged( + EntityRepositoryAdder entityRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } + ); + } +} diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs new file mode 100644 index 00000000..65f84404 --- /dev/null +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -0,0 +1,341 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Extensions; +using EntityDb.Common.Polyfills; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.Common.Streams; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Shouldly; +using Xunit; +using Version = EntityDb.Abstractions.ValueObjects.Version; + +namespace EntityDb.Common.Tests.Streams; + +[Collection(nameof(DatabaseContainerCollection))] +public class StreamTests : TestsBase +{ + public StreamTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) + : base(serviceProvider, databaseContainerFixture) + { + } + + [Fact] + public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourceIsCorrect() + { + // ARRANGE + + var committedSources = new List(); + + var sequenceMock = new MockSequence(); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + // First query checks if stream key lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerable.Empty()); + + // Second query checks if message key lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerable.Empty()); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock, committedSources)); + + serviceCollection.AddStreamRepository(); + }); + + await using var writeRepository = await GetWriteStreamRepository(serviceScope); + + var streamKey = new Key("StreamKey"); + var expectedStreamKeyLease = MultipleStreamRepository.GetStreamKeyLease(streamKey); + + var messageKey = new Key("MessageKey"); + var expectedMessageKeyLease = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey); + + var expectedDelta = new DoNothing(); + + // ACT + + await writeRepository.LoadOrCreate(streamKey); + + var staged = await writeRepository + .Stage(streamKey, messageKey, expectedDelta); + + var committed = await writeRepository + .Commit(); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.Zero); + committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(2); + committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedMessageKeyLease); + committedSources[0].Messages[0].AddLeases[1].ShouldBe(expectedStreamKeyLease); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenNewStream_WhenStagingNewMessageKey_ThenCommitReturnsTrue( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.AddStreamRepository(); + }); + + await using var writeRepository = await GetWriteStreamRepository(serviceScope); + + var streamKey = new Key("StreamKey"); + var streamKeyLease = MultipleStreamRepository.GetStreamKeyLease(streamKey); + + var messageKey = new Key("MessageKey"); + var messageKeyLease = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey); + + // ACT + + await writeRepository.LoadOrCreate(streamKey); + + var staged = await writeRepository + .Stage(streamKey, messageKey, new DoNothing()); + + var committed = await writeRepository.Commit(); + + var statePointerCount = await writeRepository.SourceRepository + .EnumerateStatePointers(new MatchingLeasesQuery(streamKeyLease, messageKeyLease)) + .CountAsync(); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + statePointerCount.ShouldBe(1); + } + + [Fact] + public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommittedSourceIsCorrect() + { + // ARRANGE + + var statePointer = Id.NewId() + Version.Zero.Next(); + + var committedSources = new List(); + + var sequenceMock = new MockSequence(); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + // First query checks if stream key lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); + + // First query checks if message key lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerable.Empty()); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock, committedSources)); + + serviceCollection.AddStreamRepository(); + }); + + await using var writeRepository = await GetWriteStreamRepository(serviceScope); + + var streamKey = new Key("StreamKey"); + var messageKey = new Key("MessageKey"); + + var expectedLease = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey); + + // ARRANGE ASSERTIONS + + statePointer.Version.ShouldNotBe(Version.Zero); + + // ACT + + await writeRepository.LoadOrCreate(streamKey); + + var staged = await writeRepository + .Stage(streamKey, messageKey, new DoNothing()); + + var committed = await writeRepository.Commit(); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].StatePointer.Id.ShouldBe(statePointer.Id); + committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.Zero); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); + committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedLease); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenExistingStream_WhenStagingNewMessageKey_ThenCommitReturnsTrue( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.AddStreamRepository(); + }); + + await using var writeRepository = await GetWriteStreamRepository(serviceScope); + + var streamKey = new Key("StreamKey"); + var streamKeyLease = MultipleStreamRepository.GetStreamKeyLease(streamKey); + + var messageKey1 = new Key("MessageKey1"); + var messageKeyLease1 = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey1); + + var messageKey2 = new Key("MessageKey2"); + var messageKeyLease2 = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey2); + + await writeRepository.LoadOrCreate(streamKey); + + var firstStaged = await writeRepository + .Stage(streamKey, messageKey1, new DoNothing()); + + var firstCommitted = await writeRepository.Commit(); + + // ARRANGE ASSERTIONS + + firstStaged.ShouldBeTrue(); + firstCommitted.ShouldBeTrue(); + + // ACT + + var secondStaged = await writeRepository + .Stage(streamKey, messageKey2, new DoNothing()); + + var secondCommitted = await writeRepository.Commit(); + + var statePointerCount = await writeRepository.SourceRepository + .EnumerateStatePointers(new MatchingLeasesQuery(streamKeyLease, messageKeyLease1, messageKeyLease2)) + .CountAsync(); + + // ASSERT + + secondStaged.ShouldBeTrue(); + secondCommitted.ShouldBeTrue(); + statePointerCount.ShouldBe(2); + } + + [Fact] + public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenStageReturnsFalse() + { + // ARRANGE + + var statePointer = Id.NewId() + Version.Zero.Next(); + + var sequenceMock = new MockSequence(); + var sourceRepositoryMock = new Mock(MockBehavior.Strict); + + // First query checks if stream key lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); + + // Second query checks if message key lease already exists + sourceRepositoryMock + .InSequence(sequenceMock) + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(sourceRepositoryMock)); + + serviceCollection.AddStreamRepository(); + }); + + await using var writeRepository = await GetWriteStreamRepository(serviceScope); + + var streamKey = new Key("StreamKey"); + var messageKey = new Key("MessageKey"); + + await writeRepository.LoadOrCreate(streamKey); + + // ACT + + var staged = await writeRepository + .Stage(streamKey, messageKey, new DoNothing()); + + // ASSERT + + staged.ShouldBeFalse(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenExistingStream_WhenStagingDuplicateMessageKey_ThenStagedReturnsFalse( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.AddStreamRepository(); + }); + + await using var writeRepository = await GetWriteStreamRepository(serviceScope); + + var streamKey = new Key("StreamKey"); + var messageKey = new Key("MessageKey"); + + await writeRepository.LoadOrCreate(streamKey); + + var stagedOnce = await writeRepository + .Stage(streamKey, messageKey, new DoNothing()); + + var committedOnce = await writeRepository.Commit(); + + // ARRANGE ASSERTIONS + + stagedOnce.ShouldBeTrue(); + committedOnce.ShouldBeTrue(); + + // ACT + + var stagedTwice = await writeRepository + .Stage(streamKey, messageKey, new DoNothing()); + + // ASSERT + + stagedTwice.ShouldBeFalse(); + } +} diff --git a/test/EntityDb.Common.Tests/TestLogger.cs b/test/EntityDb.Common.Tests/TestLogger.cs index b2404156..59f8c83f 100644 --- a/test/EntityDb.Common.Tests/TestLogger.cs +++ b/test/EntityDb.Common.Tests/TestLogger.cs @@ -32,4 +32,4 @@ void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Excep _logger.Log(logLevel, eventId, state, exception, formatter); } } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/TestSessionOptions.cs b/test/EntityDb.Common.Tests/TestSessionOptions.cs index 202b06b7..a9fa2ae4 100644 --- a/test/EntityDb.Common.Tests/TestSessionOptions.cs +++ b/test/EntityDb.Common.Tests/TestSessionOptions.cs @@ -3,8 +3,8 @@ public static class TestSessionOptions { public const string Default = nameof(Default); - + public const string Write = nameof(Write); public const string ReadOnly = nameof(ReadOnly); public const string ReadOnlySecondaryPreferred = nameof(ReadOnlySecondaryPreferred); -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 026b94c4..9e90b76a 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -1,29 +1,30 @@ -using System.Diagnostics; -using System.Reflection; -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Snapshots; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.Streams; using EntityDb.Abstractions.ValueObjects; +using EntityDb.Common.Disposables; using EntityDb.Common.Extensions; using EntityDb.Common.Polyfills; using EntityDb.Common.Tests.Implementations.Entities; using EntityDb.Common.Tests.Implementations.Projections; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Tests.Implementations.States; using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Snapshots.Sessions; using EntityDb.MongoDb.Sources.Queries; using EntityDb.MongoDb.Sources.Sessions; +using EntityDb.MongoDb.States.Sessions; using EntityDb.Redis.Extensions; -using EntityDb.Redis.Snapshots.Sessions; +using EntityDb.Redis.States.Sessions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MongoDB.Driver; using Moq; using Shouldly; +using System.Diagnostics; +using System.Reflection; using Xunit.Abstractions; using Xunit.DependencyInjection; using Xunit.DependencyInjection.Logging; @@ -37,7 +38,7 @@ public class TestsBase { public delegate void AddDependenciesDelegate(IServiceCollection serviceCollection); - private static readonly SourcesAdder[] AllSourceAdders = + private static readonly SourceRepositoryAdder[] AllSourceRepositoryAdders = { new("MongoDb", typeof(MongoDbQueryOptions), timeStamp => timeStamp.WithMillisecondPrecision(), serviceCollection => @@ -50,18 +51,12 @@ public class TestsBase serviceCollection.Configure("Count", options => { - options.FindOptions = new FindOptions - { - Collation = new Collation("en", numericOrdering: true), - }; + options.FindOptions = new FindOptions { Collation = new Collation("en", numericOrdering: true) }; }); serviceCollection.ConfigureAll(options => { - var host = databaseContainerFixture.MongoDbContainer.Hostname; - var port = databaseContainerFixture.MongoDbContainer.GetMappedPublicPort(27017); - - options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); + options.ConnectionString = databaseContainerFixture.MongoDbContainer.GetConnectionString(); options.DatabaseName = DatabaseContainerFixture.OmniParameter; options.WriteTimeout = TimeSpan.FromSeconds(1); }); @@ -114,38 +109,35 @@ protected Task RunGenericTestAsync(Type[] typeArguments, object?[] invokeParamet .ShouldNotBeNull(); } - private static SnapshotAdder MongoDbSnapshotAdder() - where TSnapshot : class, ISnapshotWithTestLogic + private static StateRepositoryAdder MongoDbStateRepositoryAdder() + where TState : class, IStateWithTestLogic { - return new SnapshotAdder($"MongoDb<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => + return new StateRepositoryAdder($"MongoDb<{typeof(TState).Name}>", typeof(TState), serviceCollection => { var databaseContainerFixture = (serviceCollection .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) .ImplementationInstance as DatabaseContainerFixture)!; - serviceCollection.AddMongoDbSnapshots(true, true); + serviceCollection.AddMongoDbStateRepository(true, true); - serviceCollection.ConfigureAll(options => + serviceCollection.ConfigureAll(options => { - var host = databaseContainerFixture.MongoDbContainer.Hostname; - var port = databaseContainerFixture.MongoDbContainer.GetMappedPublicPort(27017); - - options.ConnectionString = new UriBuilder("mongodb://", host, port).ToString(); + options.ConnectionString = databaseContainerFixture.MongoDbContainer.GetConnectionString(); options.DatabaseName = DatabaseContainerFixture.OmniParameter; - options.CollectionName = TSnapshot.MongoDbCollectionName; + options.CollectionName = TState.MongoDbCollectionName; options.WriteTimeout = TimeSpan.FromSeconds(1); }); - serviceCollection.Configure(TestSessionOptions.Write, + serviceCollection.Configure(TestSessionOptions.Write, options => { options.ReadOnly = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { options.ReadOnly = true; options.SecondaryPreferred = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => { options.ReadOnly = true; @@ -154,33 +146,33 @@ private static SnapshotAdder MongoDbSnapshotAdder() }); } - private static SnapshotAdder RedisSnapshotAdder() - where TSnapshot : class, ISnapshotWithTestLogic + private static StateRepositoryAdder RedisStateRepositoryAdder() + where TState : class, IStateWithTestLogic { - return new SnapshotAdder($"Redis<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => + return new StateRepositoryAdder($"Redis<{typeof(TState).Name}>", typeof(TState), serviceCollection => { var databaseContainerFixture = serviceCollection .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) .ImplementationInstance as DatabaseContainerFixture; - serviceCollection.AddRedisSnapshots(true); + serviceCollection.AddRedisStateRepository(true); - serviceCollection.ConfigureAll(options => + serviceCollection.ConfigureAll(options => { options.ConnectionString = databaseContainerFixture!.RedisContainer.GetConnectionString(); - options.KeyNamespace = TSnapshot.RedisKeyNamespace; + options.KeyNamespace = TState.RedisKeyNamespace; }); - serviceCollection.Configure(TestSessionOptions.Write, + serviceCollection.Configure(TestSessionOptions.Write, options => { options.ReadOnly = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { options.ReadOnly = true; options.SecondaryPreferred = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => { options.ReadOnly = true; @@ -189,115 +181,135 @@ private static SnapshotAdder RedisSnapshotAdder() }); } - private static IEnumerable AllSnapshotAdders() - where TSnapshot : class, ISnapshotWithTestLogic + private static IEnumerable AllStateRepositoryAdders() + where TState : class, IStateWithTestLogic { - yield return RedisSnapshotAdder(); - yield return MongoDbSnapshotAdder(); + yield return RedisStateRepositoryAdder(); + yield return MongoDbStateRepositoryAdder(); } - private static EntityAdder GetEntityAdder() + private static EntityRepositoryAdder GetEntityRepositoryAdder() where TEntity : IEntity { - return new EntityAdder(typeof(TEntity).Name, typeof(TEntity), - serviceCollection => { serviceCollection.AddEntity(); }); + return new EntityRepositoryAdder(typeof(TEntity).Name, typeof(TEntity), + serviceCollection => { serviceCollection.AddEntityRepository(); }); } - private static IEnumerable AllEntitySnapshotAdders() - where TEntity : class, IEntity, ISnapshotWithTestLogic + private static IEnumerable AllEntityRepositoryAdders() { - return - from snapshotAdder in AllSnapshotAdders() - let entityAdder = GetEntityAdder() - select snapshotAdder with - { - AddDependencies = snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => - { - serviceCollection.AddEntitySnapshotSourceSubscriber(TestSessionOptions.ReadOnly, - TestSessionOptions.Write); - }), - }; + yield return GetEntityRepositoryAdder(); } - private static IEnumerable AllEntityAdders() + private static ProjectionRepositoryAdder GetProjectionRepositoryAdder() + where TProjection : IProjection { - yield return GetEntityAdder(); + return new ProjectionRepositoryAdder(typeof(TProjection).Name, typeof(TProjection), serviceCollection => + { + serviceCollection.AddProjectionRepository(); + }); } - private static IEnumerable AllEntitySnapshotAdders() + private static IEnumerable AllProjectionRepositoryAdders() { - return Enumerable.Empty() - .Concat(AllEntitySnapshotAdders()); + yield return GetProjectionRepositoryAdder(); } - private static IEnumerable AllProjectionAdders() - where TProjection : class, IProjection, ISnapshotWithTestLogic + private static IEnumerable GetEntityStateRepositoryAdders() + where TEntity : class, IEntity, IStateWithTestLogic { - return AllSnapshotAdders() - .Select(snapshotAdder => snapshotAdder with - { - AddDependencies = snapshotAdder.AddDependencies + (serviceCollection => - { - serviceCollection.AddProjection(); - serviceCollection.AddProjectionSnapshotSourceSubscriber(TestSessionOptions.Write); - }), - } - ); + return + from stateRepositoryAdder in AllStateRepositoryAdders() + let entityRepositoryAdder = GetEntityRepositoryAdder() + select stateRepositoryAdder with + { + AddDependencies = stateRepositoryAdder.AddDependencies + entityRepositoryAdder.AddDependencies + + (serviceCollection => + { + serviceCollection.AddEntityStateSourceSubscriber( + TestSessionOptions.ReadOnly, + TestSessionOptions.Write); + }), + }; } - private static IEnumerable AllProjectionSnapshotAdders() + private static IEnumerable GetProjectionStateRepositoryAdders() + where TProjection : class, IProjection, IStateWithTestLogic { - return Enumerable.Empty() - .Concat(AllProjectionAdders()); + return + from stateRepositoryAdder in AllStateRepositoryAdders() + let projectionRepositoryAdder = GetProjectionRepositoryAdder() + select stateRepositoryAdder with + { + AddDependencies = stateRepositoryAdder.AddDependencies + projectionRepositoryAdder.AddDependencies + + (serviceCollection => + { + serviceCollection.AddProjectionStateSourceSubscriber( + TestSessionOptions.Write); + }), + }; } - public static IEnumerable AddSourcesAndEntity() + private static IEnumerable AllEntityStateRepositoryAdders() { - return from sourceAdder in AllSourceAdders - from entityAdder in AllEntityAdders() - select new object[] { sourceAdder, entityAdder }; + return Enumerable.Empty() + .Concat(GetEntityStateRepositoryAdders()); } - public static IEnumerable AddSource() + private static IEnumerable AllProjectionStateRepositoryAdders() { - return from sourceAdder in AllSourceAdders - select new object[] { sourceAdder }; + return Enumerable.Empty() + .Concat(GetProjectionStateRepositoryAdders()); } - public static IEnumerable AddEntity() + public static IEnumerable With_Source_Entity() { - return from entityAdder in AllEntityAdders() - select new object[] { entityAdder }; + return + from sourceRepositoryAdder in AllSourceRepositoryAdders + from entityRepositoryAdder in AllEntityRepositoryAdders() + select new object[] { sourceRepositoryAdder, entityRepositoryAdder }; } - public static IEnumerable AddEntitySnapshots() + public static IEnumerable With_Source() { - return from entitySnapshotAdder in AllEntitySnapshotAdders() - select new object[] { entitySnapshotAdder }; + return + from sourceRepositoryAdder in AllSourceRepositoryAdders + select new object[] { sourceRepositoryAdder }; } - public static IEnumerable AddProjectionSnapshots() + public static IEnumerable With_Entity() { - return from projectionSnapshotAdder in AllProjectionSnapshotAdders() - select new object[] { projectionSnapshotAdder }; + return from entityRepositoryAdder in AllEntityRepositoryAdders() + select new object[] { entityRepositoryAdder }; } - public static IEnumerable AddSourcesAndEntitySnapshots() + public static IEnumerable With_EntityState() { - return from sourceAdder in AllSourceAdders - from entitySnapshotAdder in AllEntitySnapshotAdders() - select new object[] { sourceAdder, entitySnapshotAdder }; + return from entityStateRepositoryAdder in AllEntityStateRepositoryAdders() + select new object[] { entityStateRepositoryAdder }; } - public static IEnumerable AddSourcesEntitySnapshotsAndProjectionSnapshots() + public static IEnumerable With_ProjectionState() { - return from sourceAdder in AllSourceAdders - from entitySnapshotAdder in AllEntitySnapshotAdders() - from projectionSnapshotAdder in AllProjectionSnapshotAdders() - select new object[] { sourceAdder, entitySnapshotAdder, projectionSnapshotAdder }; + return from projectionStateRepositoryAdder in AllProjectionStateRepositoryAdders() + select new object[] { projectionStateRepositoryAdder }; } - protected IServiceScope CreateServiceScope(Action? configureServices = null) + public static IEnumerable With_Source_EntityState() + { + return from sourceRepositoryAdder in AllSourceRepositoryAdders + from entityStateRepositoryAdder in AllEntityStateRepositoryAdders() + select new object[] { sourceRepositoryAdder, entityStateRepositoryAdder }; + } + + public static IEnumerable With_Source_EntityState_ProjectionState() + { + return from sourceRepositoryAdder in AllSourceRepositoryAdders + from entityStateRepositoryAdder in AllEntityStateRepositoryAdders() + from projectionStateRepositoryAdder in AllProjectionStateRepositoryAdders() + select new object[] { sourceRepositoryAdder, entityStateRepositoryAdder, projectionStateRepositoryAdder }; + } + + internal TestServiceScope CreateServiceScope(Action? configureServices = null) { var serviceCollection = new ServiceCollection(); @@ -318,7 +330,10 @@ protected IServiceScope CreateServiceScope(Action? configure startup.AddServices(serviceCollection); - if (_databaseContainerFixture != null) serviceCollection.AddSingleton(_databaseContainerFixture); + if (_databaseContainerFixture != null) + { + serviceCollection.AddSingleton(_databaseContainerFixture); + } configureServices?.Invoke(serviceCollection); @@ -328,71 +343,15 @@ protected IServiceScope CreateServiceScope(Action? configure var serviceScopeFactory = singletonServiceProvider.GetRequiredService(); - return new TestServiceScope(singletonServiceProvider, serviceScopeFactory.CreateScope()); - } - - public record Log - { - public required object[] ScopesStates { get; init; } - public required LogLevel LogLevel { get; init; } - public required EventId EventId { get; init; } - public required object? State { get; init; } - public required Exception? Exception { get; init; } - } - - private class Logger : ILogger - { - private readonly List _logs; - private readonly Stack _scopeStates = new(); - - public Logger(List logs) - { - _logs = logs; - } - - private class Scope : IDisposable - { - private readonly Logger _logger; - - public Scope(Logger logger) - { - _logger = logger; - } - - void IDisposable.Dispose() - { - _logger._scopeStates.Pop(); - } - } - - IDisposable ILogger.BeginScope(TState state) - { - _scopeStates.Push(state); - - return new Scope(this); - } - - bool ILogger.IsEnabled(LogLevel logLevel) - { - return true; - } - - void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + return new TestServiceScope { - _logs.Add(new Log - { - ScopesStates = _scopeStates.ToArray(), - LogLevel = logLevel, - EventId = eventId, - State = state, - Exception = exception, - }); - } + SingletonServiceProvider = singletonServiceProvider, + AsyncServiceScope = serviceScopeFactory.CreateAsyncScope(), + }; } - protected static ILoggerFactory GetMockedLoggerFactory(List logs) - { + { var loggerFactoryMock = new Mock(MockBehavior.Strict); loggerFactoryMock @@ -405,73 +364,6 @@ protected static ILoggerFactory GetMockedLoggerFactory(List logs) return loggerFactoryMock.Object; } - protected static (ILoggerFactory Logger, Action LoggerVerifier) GetMockedLoggerFactory() - where TException : Exception - { - var disposableMock = new Mock(MockBehavior.Strict); - - disposableMock.Setup(disposable => disposable.Dispose()); - - var loggerMock = new Mock(MockBehavior.Strict); - - loggerMock - .Setup(logger => logger.BeginScope(It.IsAny())) - .Returns(disposableMock.Object); - - loggerMock - .Setup(logger => logger.Log - ( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>() - )); - - loggerMock - .Setup(logger => logger.IsEnabled(It.IsAny())) - .Returns((LogLevel logLevel) => logLevel == LogLevel.Error); - - loggerMock - .Setup(logger => logger.Log - ( - It.Is(logLevel => logLevel == LogLevel.Error), - It.IsAny(), - It.IsAny(), - It.Is(exception => exception is TException), - It.IsAny>() - )) - .Verifiable(); - - var loggerFactoryMock = new Mock(MockBehavior.Strict); - - loggerFactoryMock - .Setup(factory => factory.CreateLogger(It.IsAny())) - .Returns(loggerMock.Object); - - loggerFactoryMock - .Setup(factory => factory.AddProvider(It.IsAny())); - - return (loggerFactoryMock.Object, Verifier); - - void Verifier(Times times) - { - loggerMock - .Verify - ( - logger => logger.Log - ( - It.Is(logLevel => logLevel == LogLevel.Error), - It.IsAny(), - It.IsAny(), - It.Is(exception => exception is TException), - It.IsAny>() - ), - times - ); - } - } - protected static ISourceSubscriber GetMockedSourceSubscriber(List committedSources) { var sourceSubscriberMock = new Mock(); @@ -486,77 +378,104 @@ protected static ISourceSubscriber GetMockedSourceSubscriber(List commit return sourceSubscriberMock.Object; } - protected static Task> GetWriteEntityRepository(IServiceScope serviceScope, bool snapshots) + protected static Task> GetWriteEntityRepository( + IServiceScope serviceScope, bool getPersistedState) { return serviceScope.ServiceProvider .GetRequiredService>() .CreateMultiple ( TestSessionOptions.Default, - TestSessionOptions.Write, - snapshots ? TestSessionOptions.Write : null + TestSessionOptions.Write, + getPersistedState ? TestSessionOptions.Write : null ); } - protected static Task> GetReadOnlyEntityRepository(IServiceScope serviceScope, bool snapshots) + protected static Task> GetReadOnlyEntityRepository( + IServiceScope serviceScope, bool getPersistedState) { return serviceScope.ServiceProvider .GetRequiredService>() .CreateMultiple ( - TestSessionOptions.Default, + TestSessionOptions.Default, TestSessionOptions.ReadOnly, - snapshots ? TestSessionOptions.ReadOnly : null + getPersistedState ? TestSessionOptions.ReadOnly : null + ); + } + + protected static Task GetWriteStreamRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .CreateMultiple + ( + TestSessionOptions.Default, + TestSessionOptions.Write ); + } + protected static Task GetReadOnlyStreamRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .CreateMultiple + ( + TestSessionOptions.Default, + TestSessionOptions.ReadOnly + ); } - protected static Task> GetWriteSnapshotRepository( + protected static Task> GetWriteStateRepository( IServiceScope serviceScope) { return serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + .GetRequiredService>() + .Create(TestSessionOptions.Write); } - - protected static Task> GetReadOnlySnapshotRepository( - IServiceScope serviceScope, bool secondaryPreferred = false) + + protected static Task> GetReadOnlyStateRepository( + IServiceScope serviceScope, bool secondary = false) { return serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(secondaryPreferred ? TestSessionOptions.ReadOnlySecondaryPreferred : TestSessionOptions.ReadOnly); + .GetRequiredService>() + .Create(secondary ? TestSessionOptions.ReadOnlySecondaryPreferred : TestSessionOptions.ReadOnly); } - + protected static Task GetWriteSourceRepository(IServiceScope serviceScope) { return serviceScope.ServiceProvider .GetRequiredService() - .CreateRepository + .Create ( TestSessionOptions.Write ); } - protected static Task GetReadOnlySourceRepository(IServiceScope serviceScope) + protected static Task GetReadOnlySourceRepository(IServiceScope serviceScope, + bool secondary = false) { return serviceScope.ServiceProvider .GetRequiredService() - .CreateRepository + .Create ( - TestSessionOptions.ReadOnly + secondary + ? TestSessionOptions.ReadOnlySecondaryPreferred + : TestSessionOptions.ReadOnly ); } - - protected static Task> GetReadOnlyProjectionRepository(IServiceScope serviceScope, bool snapshots) + + protected static Task> GetReadOnlyProjectionRepository( + IServiceScope serviceScope, bool getPersistedState) { return serviceScope.ServiceProvider .GetRequiredService>() .CreateRepository ( - snapshots ? TestSessionOptions.Write : null + getPersistedState ? TestSessionOptions.Write : null ); } - + protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( Mock sourceRepositoryMock, List? committedSources = null) { @@ -580,7 +499,7 @@ protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( new Mock(MockBehavior.Strict); sourceRepositoryFactoryMock - .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) + .Setup(factory => factory.Create(It.IsAny(), It.IsAny())) .ReturnsAsync(sourceRepositoryMock.Object); sourceRepositoryFactoryMock @@ -606,54 +525,103 @@ protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( .ReturnsAsync(true); sourceRepositoryMock - .Setup(repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + .Setup(repository => + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(deltas)); return GetMockedSourceRepositoryFactory(sourceRepositoryMock, new List()); } - protected static ISnapshotRepositoryFactory GetMockedSnapshotRepositoryFactory + protected static IStateRepositoryFactory GetMockedStateRepositoryFactory ( - TEntity? snapshot = default + TEntity? state = default ) { - var snapshotRepositoryMock = new Mock>(MockBehavior.Strict); + var stateRepositoryMock = new Mock>(MockBehavior.Strict); - snapshotRepositoryMock - .Setup(repository => repository.GetSnapshotOrDefault(It.IsAny(), It.IsAny())) - .ReturnsAsync(snapshot); + stateRepositoryMock + .Setup(repository => repository.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(state); - snapshotRepositoryMock + stateRepositoryMock .Setup(repository => repository.DisposeAsync()) .Returns(ValueTask.CompletedTask); - var snapshotRepositoryFactoryMock = new Mock>(MockBehavior.Strict); + var stateRepositoryFactoryMock = new Mock>(MockBehavior.Strict); - snapshotRepositoryFactoryMock - .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) - .ReturnsAsync(snapshotRepositoryMock.Object); + stateRepositoryFactoryMock + .Setup(factory => factory.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(stateRepositoryMock.Object); - snapshotRepositoryFactoryMock + stateRepositoryFactoryMock .Setup(factory => factory.DisposeAsync()) .Returns(ValueTask.CompletedTask); - return snapshotRepositoryFactoryMock.Object; + return stateRepositoryFactoryMock.Object; + } + + public record Log + { + public required object[] ScopesStates { get; init; } + public required LogLevel LogLevel { get; init; } + public required EventId EventId { get; init; } + public required object? State { get; init; } + public required Exception? Exception { get; init; } } - private record TestServiceScope - (ServiceProvider SingletonServiceProvider, IServiceScope ServiceScope) : IServiceScope + private class Logger(ICollection logs) : ILogger { - public IServiceProvider ServiceProvider => ServiceScope.ServiceProvider; + private readonly Stack _scopeStates = new(); + + IDisposable ILogger.BeginScope(TState state) + { + _scopeStates.Push(state); + + return new Scope(_scopeStates); + } + + bool ILogger.IsEnabled(LogLevel logLevel) + { + return true; + } + + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, + Func formatter) + { + logs.Add(new Log + { + ScopesStates = _scopeStates.ToArray(), + LogLevel = logLevel, + EventId = eventId, + State = state, + Exception = exception, + }); + } - public void Dispose() + private class Scope(Stack scopeStates) : IDisposable { - ServiceScope.Dispose(); + void IDisposable.Dispose() + { + scopeStates.Pop(); + } + } + } - SingletonServiceProvider.Dispose(); + internal record TestServiceScope : DisposableResourceBaseRecord, IServiceScope + { + public required ServiceProvider SingletonServiceProvider { get; init; } + public required AsyncServiceScope AsyncServiceScope { get; init; } + + public IServiceProvider ServiceProvider => AsyncServiceScope.ServiceProvider; + + public override async ValueTask DisposeAsync() + { + await SingletonServiceProvider.DisposeAsync(); + await AsyncServiceScope.DisposeAsync(); } } - public record SourcesAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, + public record SourceRepositoryAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, AddDependenciesDelegate AddDependencies) { public override string ToString() @@ -662,7 +630,15 @@ public override string ToString() } } - public record SnapshotAdder(string Name, Type SnapshotType, AddDependenciesDelegate AddDependencies) + public record StateRepositoryAdder(string Name, Type StateType, AddDependenciesDelegate AddDependencies) + { + public override string ToString() + { + return Name; + } + } + + public record EntityRepositoryAdder(string Name, Type EntityType, AddDependenciesDelegate AddDependencies) { public override string ToString() { @@ -670,11 +646,11 @@ public override string ToString() } } - public record EntityAdder(string Name, Type EntityType, AddDependenciesDelegate AddDependencies) + public record ProjectionRepositoryAdder(string Name, Type ProjectionType, AddDependenciesDelegate AddDependencies) { public override string ToString() { return Name; } } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs index fdbec3b2..fbb2747b 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs @@ -141,4 +141,4 @@ public void GivenGarbageTypeInformation_WhenLoadingType_ThenThrow() Should.Throw(() => typeResolver.TryResolveType(envelopeHeaders, out _)); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs index 72684d4e..165f8235 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs @@ -22,13 +22,15 @@ public void GivenPartialTypeResolverThrows_WhenResolvingType_ThenExceptionIsLogg { // ARRANGE - var (loggerFactory, loggerVerifier) = GetMockedLoggerFactory(); + var logs = new List(); + + var loggerFactory = GetMockedLoggerFactory(logs); var partialTypeResolver = new Mock(); partialTypeResolver .Setup(resolver => resolver.TryResolveType(It.IsAny(), out It.Ref.IsAny)) - .Throws(new Exception()); + .Throws(new NotImplementedException()); var typeResolver = new LifoTypeResolver(loggerFactory.CreateLogger(), new[] { partialTypeResolver.Object }); @@ -37,11 +39,11 @@ public void GivenPartialTypeResolverThrows_WhenResolvingType_ThenExceptionIsLogg Should.Throw(() => typeResolver.ResolveType(default!)); - loggerVerifier.Invoke(Times.Once()); + logs.Count(log => log.Exception is NotImplementedException).ShouldBe(1); } [Fact] - public void GivenFirstPartialTypeResolverReturnsNullAndSecondReturnsNotNull_WhenResolvingType_ThenReturnType() + public async Task GivenFirstPartialTypeResolverReturnsNullAndSecondReturnsNotNull_WhenResolvingType_ThenReturnType() { // ARRANGE @@ -71,7 +73,7 @@ public void GivenFirstPartialTypeResolverReturnsNullAndSecondReturnsNotNull_When return true; })); - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { serviceCollection.RemoveAll(typeof(IPartialTypeResolver)); serviceCollection.RemoveAll(typeof(ITypeResolver)); @@ -94,4 +96,4 @@ public void GivenFirstPartialTypeResolverReturnsNullAndSecondReturnsNotNull_When } private delegate bool TryResolveTypeDelegate(EnvelopeHeaders headers, out Type? resolvedType); -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs index 36fdb712..31649a77 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs @@ -60,8 +60,7 @@ public void GivenEmptyMemberInfoNameTypeResolver_WhenResolvingType_ThenReturnNul var envelopeHeaders = new EnvelopeHeaders(new Dictionary { - [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, - [EnvelopeHelper.MemberInfoName] = "", + [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, [EnvelopeHelper.MemberInfoName] = "", }); // ACT @@ -73,4 +72,4 @@ public void GivenEmptyMemberInfoNameTypeResolver_WhenResolvingType_ThenReturnNul resolved.ShouldBeFalse(); actualType.ShouldBeNull(); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/packages.lock.json b/test/EntityDb.Common.Tests/packages.lock.json index 75722588..47ccc887 100644 --- a/test/EntityDb.Common.Tests/packages.lock.json +++ b/test/EntityDb.Common.Tests/packages.lock.json @@ -4,9 +4,9 @@ "net7.0": { "Bogus": { "type": "Direct", - "requested": "[34.0.2, )", - "resolved": "34.0.2", - "contentHash": "2KQAuMn3fLAQ2r6jeiffZnpv0bi3GCW3iRB2v1KeHIEBu8MNTh9dBlLTZwGvM0pr+9doKkU8L5JgCURmTmT88A==" + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" }, "coverlet.collector": { "type": "Direct", @@ -16,21 +16,21 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.7.1, )", - "resolved": "17.7.1", - "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.7.1", - "Microsoft.TestPlatform.TestHost": "17.7.1" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "Shouldly": { @@ -54,40 +54,38 @@ }, "Testcontainers.MongoDb": { "type": "Direct", - "requested": "[3.0.0, )", - "resolved": "3.0.0", - "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "requested": "[3.7.0, )", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "Testcontainers.Redis": { "type": "Direct", - "requested": "[3.0.0, )", - "resolved": "3.0.0", - "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "requested": "[3.7.0, )", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "xunit": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.2.0", - "xunit.assert": "2.5.0", - "xunit.core": "[2.5.0]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.8.2, )", - "resolved": "8.8.2", - "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", @@ -106,9 +104,9 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" }, "AWSSDK.Core": { "type": "Transitive", @@ -123,10 +121,15 @@ "AWSSDK.Core": "[3.7.100.14, 4.0.0)" } }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } @@ -150,8 +153,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -160,10 +163,10 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.13" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { @@ -171,11 +174,6 @@ "resolved": "4.4.0", "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2022.3.1", - "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" - }, "MartinCostello.Logging.XUnit": { "type": "Transitive", "resolved": "0.3.0", @@ -193,8 +191,8 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -363,8 +361,8 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -471,8 +469,8 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "dependencies": { "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" @@ -480,10 +478,10 @@ }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.7.1", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -508,8 +506,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -517,29 +515,29 @@ }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", - "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", "dependencies": { "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", + "MongoDB.Bson": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { @@ -616,11 +614,6 @@ "System.IO.Pipelines": "5.0.1" } }, - "Portable.BouncyCastle": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "eZZBCABzVOek+id9Xy04HhmgykF0wZg9wpByzrWN7q8qEI0Qen9b7tfd7w8VA3dOeesumMG7C5ZPy0jk7PSRHw==" - }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", "resolved": "4.3.0", @@ -743,11 +736,25 @@ "resolved": "1.0.0", "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.122", - "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", "Pipelines.Sockets.Unofficial": "2.2.8" } }, @@ -1447,8 +1454,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1539,16 +1546,17 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", "dependencies": { - "Docker.DotNet": "3.125.13", - "Docker.DotNet.X509": "3.125.13", + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", "Microsoft.Bcl.AsyncInterfaces": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Portable.BouncyCastle": "1.9.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", "SharpZipLib": "1.4.2", - "System.Text.Json": "6.0.7" + "System.Text.Json": "6.0.9" } }, "xunit.abstractions": { @@ -1558,30 +1566,27 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.5.0]", - "xunit.extensibility.execution": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1589,17 +1594,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]" } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "entitydb.abstractions": { "type": "Project", @@ -1614,10 +1619,1655 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.inmemory": { + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.mongodb": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.provisioner": { + "type": "Project", + "dependencies": { + "EntityDb.MongoDb": "[1.0.0, )", + "System.CommandLine": "[2.0.0-beta4.22272.1, )", + "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.redis": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", + "EntityDb.Json": "[1.0.0, )", + "StackExchange.Redis": "[2.7.10, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "Bogus": { + "type": "Direct", + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" + }, + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", + "dependencies": { + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "Testcontainers.MongoDb": { + "type": "Direct", + "requested": "[3.7.0, )", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "Testcontainers.Redis": { + "type": "Direct", + "requested": "[3.7.0, )", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", + "dependencies": { + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" + } + }, + "Xunit.DependencyInjection": { + "type": "Direct", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Hosting": "6.0.0", + "xunit.extensibility.execution": "[2.4.2, 3.0.0)" + } + }, + "Xunit.DependencyInjection.Logging": { + "type": "Direct", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", + "dependencies": { + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Docker.DotNet": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.1", + "System.Buffers": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Docker.DotNet.X509": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", + "dependencies": { + "Docker.DotNet": "3.125.15" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "tq2wXyh3fL17EMF2bXgRhU7JrbO3on93MRKYxzz4JzzvuGSA1l0W3GI9/tl8EO89TH+KWEymP7bcFway6z9fXg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "DjYkzqvhiHCq38LW71PcIxXk6nhtV6VySP9yDcSO0goPl7YCU1VG1f2Wbgy58lkA10pWkjHCblZPUyboCB93ZA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lB0Hb2V4+RUHy+LjEcqEr4EcV4RWc9EnjAV2GdtWQEdljQX+R4hGREftI7sInU9okP93pDrJiaj6QUJ6ZsslOA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M8VzD0ni5VarIRT8njnwK4K2WSAo0kZH4Zc3mKcSGkP4CjDZ91T9ZEFmmwhmo4z7x8AFq+tW0WFi9wX+K2cxkQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.0", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Logging.Console": "6.0.0", + "Microsoft.Extensions.Logging.Debug": "6.0.0", + "Microsoft.Extensions.Logging.EventLog": "6.0.0", + "Microsoft.Extensions.Logging.EventSource": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", + "dependencies": { + "NuGet.Frameworks": "6.5.0", + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.8.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", + "dependencies": { + "System.IO.Pipelines": "5.0.1" + } + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "SharpZipLib": { + "type": "Transitive", + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Concurrent": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.CommandLine": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" + }, + "System.CommandLine.NamingConventionBinder": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "ux2eUA/syF+JtlpMDc/Lsd6PBIBuwjH3AvHnestoh5uD0WKT5b+wkQxDWVCqp9qgVjMBTLNhX19ZYFtenunt9A==", + "dependencies": { + "System.CommandLine": "2.0.0-beta4.22272.1" + } + }, + "System.Console": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.Tracing": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Calendars": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + } + }, + "System.IO.Compression.ZipFile": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.IO.FileSystem": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.FileSystem.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Net.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Net.Sockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Runtime.Numerics": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Security.Cryptography.Csp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encoding.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Threading.Timer": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "Testcontainers": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", + "dependencies": { + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.9" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", + "dependencies": { + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.extensibility.core": "[2.6.5]" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1632,7 +3282,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.21.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1650,7 +3300,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.122, )", + "StackExchange.Redis": "[2.7.10, )", "System.Linq.Async": "[6.0.1, )" } } diff --git a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs index cb9a248a..b5dff9a2 100644 --- a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs +++ b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs @@ -24,14 +24,14 @@ protected override BsonDocument GenerateCorruptedSerializedData() [Theory] [InlineData(true)] [InlineData(false)] - public void GivenTypeDiscriminatorShouldBeRemovedOption_ThereBsonDocumentMatchesOption( + public async Task GivenTypeDiscriminatorShouldBeRemovedOption_ThereBsonDocumentMatchesOption( bool removeTypeDiscriminatorProperty) { // ARRANGE var expectedContainsTypeDiscriminatorProperty = !removeTypeDiscriminatorProperty; - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { serviceCollection.RemoveAll(typeof(IEnvelopeService)); @@ -53,4 +53,4 @@ public void GivenTypeDiscriminatorShouldBeRemovedOption_ThereBsonDocumentMatches actualContainsTypeDiscriminatorProperty.ShouldBe(expectedContainsTypeDiscriminatorProperty); } -} \ No newline at end of file +} diff --git a/test/EntityDb.MongoDb.Tests/Properties/AssemblyInfo.cs b/test/EntityDb.MongoDb.Tests/Properties/AssemblyInfo.cs index 67eb74b5..3a1b16c4 100644 --- a/test/EntityDb.MongoDb.Tests/Properties/AssemblyInfo.cs +++ b/test/EntityDb.MongoDb.Tests/Properties/AssemblyInfo.cs @@ -2,4 +2,4 @@ using Xunit; [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] \ No newline at end of file +[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] diff --git a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs index 3ec0ffec..2fa28e15 100644 --- a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs +++ b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs @@ -17,23 +17,21 @@ public async Task WhenExecutingWriteMethods_ThenThrow() { // ARRANGE - var mongoSession = new MongoSession(default!, default!, default!, new MongoDbSourceSessionOptions - { - ReadOnly = true, - }); + var mongoSession = new MongoSession(default!, default!, default!, + new MongoDbSourceSessionOptions { ReadOnly = true }); // ASSERT - await Should.ThrowAsync(() => + await Should.ThrowAsync(() => mongoSession.Insert(default!, default!, default)); - await Should.ThrowAsync(() => + await Should.ThrowAsync(() => mongoSession.Delete(default!, default!, default)); - Should.Throw(() => mongoSession.StartTransaction()); + Should.Throw(() => mongoSession.StartTransaction()); - await Should.ThrowAsync(() => mongoSession.CommitTransaction(default)); + await Should.ThrowAsync(() => mongoSession.CommitTransaction(default)); - await Should.ThrowAsync(() => mongoSession.AbortTransaction()); + await Should.ThrowAsync(() => mongoSession.AbortTransaction()); } -} \ No newline at end of file +} diff --git a/test/EntityDb.MongoDb.Tests/Startup.cs b/test/EntityDb.MongoDb.Tests/Startup.cs index 8f65e8b6..6e0818ad 100644 --- a/test/EntityDb.MongoDb.Tests/Startup.cs +++ b/test/EntityDb.MongoDb.Tests/Startup.cs @@ -12,4 +12,4 @@ public override void AddServices(IServiceCollection serviceCollection) serviceCollection.AddBsonDocumentEnvelopeService(true); } -} \ No newline at end of file +} diff --git a/test/EntityDb.MongoDb.Tests/packages.lock.json b/test/EntityDb.MongoDb.Tests/packages.lock.json index 81c3796d..67587266 100644 --- a/test/EntityDb.MongoDb.Tests/packages.lock.json +++ b/test/EntityDb.MongoDb.Tests/packages.lock.json @@ -4,9 +4,9 @@ "net7.0": { "Bogus": { "type": "Direct", - "requested": "[34.0.2, )", - "resolved": "34.0.2", - "contentHash": "2KQAuMn3fLAQ2r6jeiffZnpv0bi3GCW3iRB2v1KeHIEBu8MNTh9dBlLTZwGvM0pr+9doKkU8L5JgCURmTmT88A==" + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" }, "coverlet.collector": { "type": "Direct", @@ -16,21 +16,21 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.7.1, )", - "resolved": "17.7.1", - "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.7.1", - "Microsoft.TestPlatform.TestHost": "17.7.1" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "Shouldly": { @@ -54,20 +54,20 @@ }, "xunit": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.2.0", - "xunit.assert": "2.5.0", - "xunit.core": "[2.5.0]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.8.2, )", - "resolved": "8.8.2", - "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", @@ -86,9 +86,9 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" }, "AWSSDK.Core": { "type": "Transitive", @@ -103,10 +103,15 @@ "AWSSDK.Core": "[3.7.100.14, 4.0.0)" } }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } @@ -130,8 +135,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -140,10 +145,10 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.13" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { @@ -151,11 +156,6 @@ "resolved": "4.4.0", "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2022.3.1", - "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" - }, "MartinCostello.Logging.XUnit": { "type": "Transitive", "resolved": "0.3.0", @@ -173,8 +173,8 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -343,8 +343,8 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -451,8 +451,8 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "dependencies": { "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" @@ -460,10 +460,10 @@ }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.7.1", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -488,8 +488,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -497,29 +497,29 @@ }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", - "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", "dependencies": { "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", + "MongoDB.Bson": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { @@ -596,11 +596,6 @@ "System.IO.Pipelines": "5.0.1" } }, - "Portable.BouncyCastle": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "eZZBCABzVOek+id9Xy04HhmgykF0wZg9wpByzrWN7q8qEI0Qen9b7tfd7w8VA3dOeesumMG7C5ZPy0jk7PSRHw==" - }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", "resolved": "4.3.0", @@ -723,11 +718,25 @@ "resolved": "1.0.0", "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.122", - "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", "Pipelines.Sockets.Unofficial": "2.2.8" } }, @@ -1427,8 +1436,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1519,34 +1528,33 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", "dependencies": { - "Docker.DotNet": "3.125.13", - "Docker.DotNet.X509": "3.125.13", + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", "Microsoft.Bcl.AsyncInterfaces": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Portable.BouncyCastle": "1.9.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", "SharpZipLib": "1.4.2", - "System.Text.Json": "6.0.7" + "System.Text.Json": "6.0.9" } }, "Testcontainers.MongoDb": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "Testcontainers.Redis": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "xunit.abstractions": { @@ -1556,30 +1564,1695 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", + "dependencies": { + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.extensibility.core": "[2.6.5]" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common.tests": { + "type": "Project", + "dependencies": { + "Bogus": "[35.3.0, )", + "EntityDb.Common": "[1.0.0, )", + "EntityDb.Provisioner": "[1.0.0, )", + "EntityDb.Redis": "[1.0.0, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", + "Shouldly": "[4.2.1, )", + "System.Linq.Async": "[6.0.1, )", + "Testcontainers.MongoDb": "[3.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.6.5, )" + } + }, + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.mongodb": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.provisioner": { + "type": "Project", + "dependencies": { + "EntityDb.MongoDb": "[1.0.0, )", + "System.CommandLine": "[2.0.0-beta4.22272.1, )", + "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.redis": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "EntityDb.Json": "[1.0.0, )", + "StackExchange.Redis": "[2.7.10, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "Bogus": { + "type": "Direct", + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" + }, + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", + "dependencies": { + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", + "dependencies": { + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" + } + }, + "Xunit.DependencyInjection": { + "type": "Direct", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Hosting": "6.0.0", + "xunit.extensibility.execution": "[2.4.2, 3.0.0)" + } + }, + "Xunit.DependencyInjection.Logging": { + "type": "Direct", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", + "dependencies": { + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Docker.DotNet": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.1", + "System.Buffers": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Docker.DotNet.X509": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", + "dependencies": { + "Docker.DotNet": "3.125.15" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "tq2wXyh3fL17EMF2bXgRhU7JrbO3on93MRKYxzz4JzzvuGSA1l0W3GI9/tl8EO89TH+KWEymP7bcFway6z9fXg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", "dependencies": { - "NETStandard.Library": "1.6.1" + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" } }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "DjYkzqvhiHCq38LW71PcIxXk6nhtV6VySP9yDcSO0goPl7YCU1VG1f2Wbgy58lkA10pWkjHCblZPUyboCB93ZA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lB0Hb2V4+RUHy+LjEcqEr4EcV4RWc9EnjAV2GdtWQEdljQX+R4hGREftI7sInU9okP93pDrJiaj6QUJ6ZsslOA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M8VzD0ni5VarIRT8njnwK4K2WSAo0kZH4Zc3mKcSGkP4CjDZ91T9ZEFmmwhmo4z7x8AFq+tW0WFi9wX+K2cxkQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.0", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Logging.Console": "6.0.0", + "Microsoft.Extensions.Logging.Debug": "6.0.0", + "Microsoft.Extensions.Logging.EventLog": "6.0.0", + "Microsoft.Extensions.Logging.EventSource": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", + "dependencies": { + "NuGet.Frameworks": "6.5.0", + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.8.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", + "dependencies": { + "System.IO.Pipelines": "5.0.1" + } + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "SharpZipLib": { + "type": "Transitive", + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Concurrent": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.CommandLine": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" + }, + "System.CommandLine.NamingConventionBinder": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "ux2eUA/syF+JtlpMDc/Lsd6PBIBuwjH3AvHnestoh5uD0WKT5b+wkQxDWVCqp9qgVjMBTLNhX19ZYFtenunt9A==", + "dependencies": { + "System.CommandLine": "2.0.0-beta4.22272.1" + } + }, + "System.Console": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.Tracing": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Calendars": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + } + }, + "System.IO.Compression.ZipFile": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.IO.FileSystem": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.FileSystem.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Net.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Net.Sockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Runtime.Numerics": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Security.Cryptography.Csp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encoding.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Threading.Timer": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "Testcontainers": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", + "dependencies": { + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.9" + } + }, + "Testcontainers.MongoDb": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "Testcontainers.Redis": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, "xunit.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.5.0]", - "xunit.extensibility.execution": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1587,17 +3260,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]" } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "entitydb.abstractions": { "type": "Project", @@ -1615,19 +3288,19 @@ "entitydb.common.tests": { "type": "Project", "dependencies": { - "Bogus": "[34.0.2, )", + "Bogus": "[35.3.0, )", "EntityDb.Common": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", - "Microsoft.NET.Test.Sdk": "[17.7.1, )", - "Moq": "[4.18.2, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", - "Testcontainers.MongoDb": "[3.0.0, )", - "Testcontainers.Redis": "[3.0.0, )", - "Xunit.DependencyInjection": "[8.8.2, )", + "Testcontainers.MongoDb": "[3.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", "Xunit.DependencyInjection.Logging": "[8.1.0, )", - "xunit": "[2.5.0, )" + "xunit": "[2.6.5, )" } }, "entitydb.json": { @@ -1641,7 +3314,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.21.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1659,7 +3332,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.122, )", + "StackExchange.Redis": "[2.7.10, )", "System.Linq.Async": "[6.0.1, )" } } diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs index 6e47f48b..7142502b 100644 --- a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs +++ b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs @@ -1,4 +1,4 @@ -using EntityDb.Common.Tests.Agents; +using EntityDb.Common.Tests.Sources.Agents; using EntityDb.Mvc.Agents; using EntityDb.Mvc.Tests.Seeder; using Microsoft.AspNetCore.Http; @@ -44,18 +44,12 @@ protected override IEnumerable GetAgentAccessorOptions { new HttpContextSeederOptions { - Headers = new Dictionary - { - ["Content-Type"] = new[] { "application/json" }, - }, + Headers = new Dictionary { ["Content-Type"] = new[] { "application/json" } }, HasIpAddress = true, }, new HttpContextSeederOptions { - Headers = new Dictionary - { - ["Content-Type"] = new[] { "application/json" }, - }, + Headers = new Dictionary { ["Content-Type"] = new[] { "application/json" } }, HasIpAddress = false, }, }; @@ -67,4 +61,4 @@ protected override IEnumerable GetAgentAccessorOptions ? null : httpContextAgentSignature.ApplicationInfo; } -} \ No newline at end of file +} diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs index 77e6b6d0..04e8b248 100644 --- a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs +++ b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs @@ -15,17 +15,11 @@ public void GivenNoRedactedHeaders_WhenHttpContextHasHeader_ThenAgentSignatureHa const string headerName = nameof(headerName); const string headerValue = nameof(headerValue); - var httpContextAgentOptions = new HttpContextAgentSignatureOptions - { - RedactedHeaders = Array.Empty(), - }; + var httpContextAgentOptions = new HttpContextAgentSignatureOptions { RedactedHeaders = Array.Empty() }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions { - Headers = new Dictionary - { - [headerName] = new[] { headerValue }, - }, + Headers = new Dictionary { [headerName] = new[] { headerValue } }, }); // ACT @@ -51,16 +45,12 @@ public void GivenRedactedHeader_WhenHttpContextContainsOnlyThatHeader_ThenAgentS var httpContextAgentOptions = new HttpContextAgentSignatureOptions { - RedactedHeaders = new[] { headerName }, - RedactedValue = redactedValue, + RedactedHeaders = new[] { headerName }, RedactedValue = redactedValue, }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions { - Headers = new Dictionary - { - [headerName] = new[] { headerValue }, - }, + Headers = new Dictionary { [headerName] = new[] { headerValue } }, }); // ACT @@ -121,8 +111,7 @@ public void var httpContextAgentOptions = new HttpContextAgentSignatureOptions { - RedactedQueryStringParams = new[] { queryStringParamName }, - RedactedValue = redactedValue, + RedactedQueryStringParams = new[] { queryStringParamName }, RedactedValue = redactedValue, }; var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions @@ -144,4 +133,4 @@ public void signature.Request.QueryStringParams[0].Values.Length.ShouldBe(1); signature.Request.QueryStringParams[0].Values[0].ShouldBe(redactedValue); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Mvc.Tests/Properties/AssemblyInfo.cs b/test/EntityDb.Mvc.Tests/Properties/AssemblyInfo.cs index 67eb74b5..3a1b16c4 100644 --- a/test/EntityDb.Mvc.Tests/Properties/AssemblyInfo.cs +++ b/test/EntityDb.Mvc.Tests/Properties/AssemblyInfo.cs @@ -2,4 +2,4 @@ using Xunit; [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] \ No newline at end of file +[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] diff --git a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs index 496f61be..c95c0571 100644 --- a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs +++ b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs @@ -102,4 +102,4 @@ public static HttpContext CreateHttpContext(HttpContextSeederOptions httpContext return httpContextMock.Object; } -} \ No newline at end of file +} diff --git a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs index cd995628..c8c319ba 100644 --- a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs +++ b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs @@ -13,4 +13,4 @@ public class HttpContextSeederOptions // Connection public bool HasIpAddress { get; init; } -} \ No newline at end of file +} diff --git a/test/EntityDb.Mvc.Tests/Startup.cs b/test/EntityDb.Mvc.Tests/Startup.cs index 8a7f6926..62a83457 100644 --- a/test/EntityDb.Mvc.Tests/Startup.cs +++ b/test/EntityDb.Mvc.Tests/Startup.cs @@ -14,4 +14,4 @@ public override void AddServices(IServiceCollection serviceCollection) serviceCollection.AddHttpContextAgentAccessor(); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Mvc.Tests/packages.lock.json b/test/EntityDb.Mvc.Tests/packages.lock.json index 810baf2c..a40c469f 100644 --- a/test/EntityDb.Mvc.Tests/packages.lock.json +++ b/test/EntityDb.Mvc.Tests/packages.lock.json @@ -4,9 +4,9 @@ "net7.0": { "Bogus": { "type": "Direct", - "requested": "[34.0.2, )", - "resolved": "34.0.2", - "contentHash": "2KQAuMn3fLAQ2r6jeiffZnpv0bi3GCW3iRB2v1KeHIEBu8MNTh9dBlLTZwGvM0pr+9doKkU8L5JgCURmTmT88A==" + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" }, "coverlet.collector": { "type": "Direct", @@ -16,21 +16,21 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.7.1, )", - "resolved": "17.7.1", - "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.7.1", - "Microsoft.TestPlatform.TestHost": "17.7.1" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "Shouldly": { @@ -54,20 +54,20 @@ }, "xunit": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.2.0", - "xunit.assert": "2.5.0", - "xunit.core": "[2.5.0]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.8.2, )", - "resolved": "8.8.2", - "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", @@ -86,9 +86,9 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" }, "AWSSDK.Core": { "type": "Transitive", @@ -103,10 +103,15 @@ "AWSSDK.Core": "[3.7.100.14, 4.0.0)" } }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } @@ -130,8 +135,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -140,10 +145,10 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.13" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { @@ -151,11 +156,6 @@ "resolved": "4.4.0", "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2022.3.1", - "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" - }, "MartinCostello.Logging.XUnit": { "type": "Transitive", "resolved": "0.3.0", @@ -173,8 +173,8 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -343,8 +343,8 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -451,8 +451,8 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "dependencies": { "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" @@ -460,10 +460,10 @@ }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.7.1", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -488,8 +488,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -497,29 +497,29 @@ }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", - "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", "dependencies": { "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", + "MongoDB.Bson": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { @@ -596,11 +596,6 @@ "System.IO.Pipelines": "5.0.1" } }, - "Portable.BouncyCastle": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "eZZBCABzVOek+id9Xy04HhmgykF0wZg9wpByzrWN7q8qEI0Qen9b7tfd7w8VA3dOeesumMG7C5ZPy0jk7PSRHw==" - }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", "resolved": "4.3.0", @@ -723,11 +718,25 @@ "resolved": "1.0.0", "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.122", - "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", "Pipelines.Sockets.Unofficial": "2.2.8" } }, @@ -1427,8 +1436,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1519,34 +1528,33 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", "dependencies": { - "Docker.DotNet": "3.125.13", - "Docker.DotNet.X509": "3.125.13", + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", "Microsoft.Bcl.AsyncInterfaces": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Portable.BouncyCastle": "1.9.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", "SharpZipLib": "1.4.2", - "System.Text.Json": "6.0.7" + "System.Text.Json": "6.0.9" } }, "Testcontainers.MongoDb": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "Testcontainers.Redis": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "xunit.abstractions": { @@ -1556,30 +1564,1702 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", + "dependencies": { + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.extensibility.core": "[2.6.5]" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common.tests": { + "type": "Project", + "dependencies": { + "Bogus": "[35.3.0, )", + "EntityDb.Common": "[1.0.0, )", + "EntityDb.Provisioner": "[1.0.0, )", + "EntityDb.Redis": "[1.0.0, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", + "Shouldly": "[4.2.1, )", + "System.Linq.Async": "[6.0.1, )", + "Testcontainers.MongoDb": "[3.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.6.5, )" + } + }, + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.mongodb": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.mvc": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.provisioner": { + "type": "Project", + "dependencies": { + "EntityDb.MongoDb": "[1.0.0, )", + "System.CommandLine": "[2.0.0-beta4.22272.1, )", + "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.redis": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "EntityDb.Json": "[1.0.0, )", + "StackExchange.Redis": "[2.7.10, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "Bogus": { + "type": "Direct", + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" + }, + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", + "dependencies": { + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", + "dependencies": { + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" + } + }, + "Xunit.DependencyInjection": { + "type": "Direct", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Hosting": "6.0.0", + "xunit.extensibility.execution": "[2.4.2, 3.0.0)" + } + }, + "Xunit.DependencyInjection.Logging": { + "type": "Direct", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", + "dependencies": { + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Docker.DotNet": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.1", + "System.Buffers": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Docker.DotNet.X509": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", + "dependencies": { + "Docker.DotNet": "3.125.15" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "tq2wXyh3fL17EMF2bXgRhU7JrbO3on93MRKYxzz4JzzvuGSA1l0W3GI9/tl8EO89TH+KWEymP7bcFway6z9fXg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", "dependencies": { - "NETStandard.Library": "1.6.1" + "Microsoft.Extensions.Primitives": "6.0.0" } }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "DjYkzqvhiHCq38LW71PcIxXk6nhtV6VySP9yDcSO0goPl7YCU1VG1f2Wbgy58lkA10pWkjHCblZPUyboCB93ZA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lB0Hb2V4+RUHy+LjEcqEr4EcV4RWc9EnjAV2GdtWQEdljQX+R4hGREftI7sInU9okP93pDrJiaj6QUJ6ZsslOA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M8VzD0ni5VarIRT8njnwK4K2WSAo0kZH4Zc3mKcSGkP4CjDZ91T9ZEFmmwhmo4z7x8AFq+tW0WFi9wX+K2cxkQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.0", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Logging.Console": "6.0.0", + "Microsoft.Extensions.Logging.Debug": "6.0.0", + "Microsoft.Extensions.Logging.EventLog": "6.0.0", + "Microsoft.Extensions.Logging.EventSource": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", + "dependencies": { + "NuGet.Frameworks": "6.5.0", + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.8.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", + "dependencies": { + "System.IO.Pipelines": "5.0.1" + } + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "SharpZipLib": { + "type": "Transitive", + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Concurrent": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.CommandLine": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" + }, + "System.CommandLine.NamingConventionBinder": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "ux2eUA/syF+JtlpMDc/Lsd6PBIBuwjH3AvHnestoh5uD0WKT5b+wkQxDWVCqp9qgVjMBTLNhX19ZYFtenunt9A==", + "dependencies": { + "System.CommandLine": "2.0.0-beta4.22272.1" + } + }, + "System.Console": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.Tracing": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Calendars": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + } + }, + "System.IO.Compression.ZipFile": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.IO.FileSystem": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.FileSystem.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Net.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Net.Sockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Runtime.Numerics": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Security.Cryptography.Csp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encoding.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Threading.Timer": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "Testcontainers": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", + "dependencies": { + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.9" + } + }, + "Testcontainers.MongoDb": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "Testcontainers.Redis": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, "xunit.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.5.0]", - "xunit.extensibility.execution": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1587,17 +3267,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]" } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "entitydb.abstractions": { "type": "Project", @@ -1615,19 +3295,19 @@ "entitydb.common.tests": { "type": "Project", "dependencies": { - "Bogus": "[34.0.2, )", + "Bogus": "[35.3.0, )", "EntityDb.Common": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", - "Microsoft.NET.Test.Sdk": "[17.7.1, )", - "Moq": "[4.18.2, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", - "Testcontainers.MongoDb": "[3.0.0, )", - "Testcontainers.Redis": "[3.0.0, )", - "Xunit.DependencyInjection": "[8.8.2, )", + "Testcontainers.MongoDb": "[3.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", "Xunit.DependencyInjection.Logging": "[8.1.0, )", - "xunit": "[2.5.0, )" + "xunit": "[2.6.5, )" } }, "entitydb.json": { @@ -1641,7 +3321,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.21.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1666,7 +3346,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.122, )", + "StackExchange.Redis": "[2.7.10, )", "System.Linq.Async": "[6.0.1, )" } } diff --git a/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs b/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs index f56b3976..0e5f6166 100644 --- a/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs +++ b/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs @@ -1,5 +1,5 @@ -using System.Text; -using EntityDb.Common.Tests.Envelopes; +using EntityDb.Common.Tests.Envelopes; +using System.Text; namespace EntityDb.Redis.Tests.Envelopes; @@ -15,4 +15,4 @@ protected override byte[] GenerateCorruptedSerializedData() return Encoding.UTF8.GetBytes(invalidJson); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Redis.Tests/Properties/AssemblyInfo.cs b/test/EntityDb.Redis.Tests/Properties/AssemblyInfo.cs index 67eb74b5..3a1b16c4 100644 --- a/test/EntityDb.Redis.Tests/Properties/AssemblyInfo.cs +++ b/test/EntityDb.Redis.Tests/Properties/AssemblyInfo.cs @@ -2,4 +2,4 @@ using Xunit; [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] \ No newline at end of file +[assembly: ExcludeFromCodeCoverage(Justification = "Do not report coverage for Test projects.")] diff --git a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs index ddaaad2a..1473ae8f 100644 --- a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs +++ b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs @@ -1,6 +1,6 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests; -using EntityDb.Redis.Snapshots.Sessions; +using EntityDb.Redis.States.Sessions; using Shouldly; using Xunit; @@ -17,16 +17,14 @@ public async Task WhenExecutingWriteMethods_ThenThrow() { // ARRANGE - var readOnlyRedisSession = new RedisSession(default!, default!, new RedisSnapshotSessionOptions - { - ReadOnly = true, - }); + var readOnlyRedisSession = + new RedisSession(default!, default!, new RedisStateSessionOptions { ReadOnly = true }); // ASSERT - await Should.ThrowAsync(() => - readOnlyRedisSession.Insert(default!, default!)); + await Should.ThrowAsync(() => + readOnlyRedisSession.Upsert(default!, default!)); - await Should.ThrowAsync(() => readOnlyRedisSession.Delete(default!)); + await Should.ThrowAsync(() => readOnlyRedisSession.Delete(default!)); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Redis.Tests/Startup.cs b/test/EntityDb.Redis.Tests/Startup.cs index 7dcc111c..966339b6 100644 --- a/test/EntityDb.Redis.Tests/Startup.cs +++ b/test/EntityDb.Redis.Tests/Startup.cs @@ -12,4 +12,4 @@ public override void AddServices(IServiceCollection serviceCollection) serviceCollection.AddJsonElementEnvelopeService(); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Redis.Tests/packages.lock.json b/test/EntityDb.Redis.Tests/packages.lock.json index 81c3796d..67587266 100644 --- a/test/EntityDb.Redis.Tests/packages.lock.json +++ b/test/EntityDb.Redis.Tests/packages.lock.json @@ -4,9 +4,9 @@ "net7.0": { "Bogus": { "type": "Direct", - "requested": "[34.0.2, )", - "resolved": "34.0.2", - "contentHash": "2KQAuMn3fLAQ2r6jeiffZnpv0bi3GCW3iRB2v1KeHIEBu8MNTh9dBlLTZwGvM0pr+9doKkU8L5JgCURmTmT88A==" + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" }, "coverlet.collector": { "type": "Direct", @@ -16,21 +16,21 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.7.1, )", - "resolved": "17.7.1", - "contentHash": "o1qyqDOR8eMuQrC1e5EMMcE+Wm3rwES5aHNWaJpi2A5qwVOru23zsdXkndT6hgl79QsJsqKp+/RNcayIzpHjvA==", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.7.1", - "Microsoft.TestPlatform.TestHost": "17.7.1" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "Shouldly": { @@ -54,20 +54,20 @@ }, "xunit": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "f2V5wuAdoaq0mRTt9UBmPbVex9HcwFYn+y7WaKUz5Xpakcrv7lhtQWBJUWNY4N3Z+o+atDBLyAALM1QWx04C6Q==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.2.0", - "xunit.assert": "2.5.0", - "xunit.core": "[2.5.0]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.8.2, )", - "resolved": "8.8.2", - "contentHash": "YKhfe9qmYxWkSw+NNc8HfWrqXkVOncghrXjHVY7X0LW1RX6CO7HT71ggPOCecrU4lzjNA6j3nw8VD/jWeq42Mg==", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "6.0.0", "Microsoft.Extensions.Hosting": "6.0.0", @@ -86,9 +86,9 @@ }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.5.0, )", - "resolved": "2.5.0", - "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" }, "AWSSDK.Core": { "type": "Transitive", @@ -103,10 +103,15 @@ "AWSSDK.Core": "[3.7.100.14, 4.0.0)" } }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } @@ -130,8 +135,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "p1DrW2Sw4ND2jFlIvpHB8/pY5o5HIkDalbGAI8tUvqjJR6n0/ubos7kDGWI+Xbx9+L3US3SUR8r59Zwq+ZxBvw==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -140,10 +145,10 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.13", - "contentHash": "+rhZU3AK4cgiRJQwramWZ2ud/tfGDZ5L7sQ6m1AtvFTUi7gEafHahRwLmdeGOov3NgW8aKD+TFRi9Ht97B+jBA==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.13" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { @@ -151,11 +156,6 @@ "resolved": "4.4.0", "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2022.3.1", - "contentHash": "11nsS3+lFICGkztVs9PP2NRItxf9fF+qD6xEW/T0YGto52zj07wseUeBdMAU1ah9HNVTDZyRC1u4NWdtJScwhw==" - }, "MartinCostello.Logging.XUnit": { "type": "Transitive", "resolved": "0.3.0", @@ -173,8 +173,8 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "NmGwM2ZJy4CAMdJYIp53opUjnXsMbzASX5oQzgxORicJsgz5Lp50fnRI8OmQ/kYNg6dHfr3IjuUoXbsotDX+KA==" + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -343,8 +343,8 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -451,8 +451,8 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "nDmV03yHIdAiG5J3ZEjMyJM2XDjmWORuKgbrGzqlAipBEjUuy5Z5S7WwSqUv9OiaUrtCn9dNYmjfMELUi08leQ==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "dependencies": { "NuGet.Frameworks": "6.5.0", "System.Reflection.Metadata": "1.6.0" @@ -460,10 +460,10 @@ }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.7.1", - "contentHash": "WCU1NyBarz0tih+I9K5OWN1dVo3z562Iek/VAqWNWRFWw1GeUGqB61iixrBvZO77sjTtBc1cXO8H95uImfmEdw==", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.7.1", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -488,8 +488,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "QT+D1I3Jz6r6S6kCgJD1L9dRCLVJCKlkGRkA+tJ7uLpHRmjDNcNKy4D1T+L9gQrjl95lDN9PHdwEytdvCW/jzA==", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -497,29 +497,29 @@ }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "VxKj1wuhadiXhaXkykCWRgsYOysdaOYJ202hJFz25UjkrqC/tHA8RS4hdS5HYfGWoI//fypBXnxZCkEjXLXdfw==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", - "MongoDB.Driver.Core": "2.21.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.21.0", - "contentHash": "Ac44U3bQfinmdH5KNFjTidJe9LKW87SxkXJ3YuIUJQMITEc4083YF1yvjJxaSeYF9er0YgHSmwhHpsZv0Fwplg==", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", "dependencies": { "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.21.0", + "MongoDB.Bson": "2.23.1", "MongoDB.Libmongocrypt": "1.8.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { @@ -596,11 +596,6 @@ "System.IO.Pipelines": "5.0.1" } }, - "Portable.BouncyCastle": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "eZZBCABzVOek+id9Xy04HhmgykF0wZg9wpByzrWN7q8qEI0Qen9b7tfd7w8VA3dOeesumMG7C5ZPy0jk7PSRHw==" - }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", "resolved": "4.3.0", @@ -723,11 +718,25 @@ "resolved": "1.0.0", "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, "StackExchange.Redis": { "type": "Transitive", - "resolved": "2.6.122", - "contentHash": "wp7mvGpFXaevfZ07/SDeh/6YHUJEgwJIGyjbDWKBYbPwKMJQYFz9zFEmBptqtVzqvSgft5nlewwutoaMaG0LPA==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", "Pipelines.Sockets.Unofficial": "2.2.8" } }, @@ -1427,8 +1436,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" @@ -1519,34 +1528,33 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "0WaQeEkzAuRgSof6HsVxAS565JsCjYW63phSa3BWu/GSH38VKEKsjP/ZjMOD+zM+BrHuYwkRYAtaJLZxsTo3lA==", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", "dependencies": { - "Docker.DotNet": "3.125.13", - "Docker.DotNet.X509": "3.125.13", + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", "Microsoft.Bcl.AsyncInterfaces": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Portable.BouncyCastle": "1.9.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", "SharpZipLib": "1.4.2", - "System.Text.Json": "6.0.7" + "System.Text.Json": "6.0.9" } }, "Testcontainers.MongoDb": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "AzA8Uwf/VIhdLX/M2mQTO9PB4ZAKJY0T4weJdSx+ul79czU/+Scm6pn3eNF6c0drrPWPesMnvNHrMhvFzALh+w==", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "Testcontainers.Redis": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "LDlBqKazcA0tThJfrZfxmKr+8EmKLhXQqqivWgmYJyTODFsB9gAA19bpqD6ZsOuq++92izGihcoA/zMg/qubpQ==", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", "dependencies": { - "JetBrains.Annotations": "2022.3.1", - "Testcontainers": "3.0.0" + "Testcontainers": "3.7.0" } }, "xunit.abstractions": { @@ -1556,30 +1564,1695 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "d3dehV/DASLRlR8stWQmbPPjfYC2tct50Evav+OlsJMkfFqkhYvzO1k0s81lk0px8O0knZU/FqC8SqbXOtn+hw==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "wN84pKX5jzfpgJ0bB6arrCA/oelBeYLCpnQ9Wj5xGEVPydKzVSDY5tEatFLHE/rO0+0RC+I4H5igGE118jRh1w==", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", + "dependencies": { + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.extensibility.core": "[2.6.5]" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common.tests": { + "type": "Project", + "dependencies": { + "Bogus": "[35.3.0, )", + "EntityDb.Common": "[1.0.0, )", + "EntityDb.Provisioner": "[1.0.0, )", + "EntityDb.Redis": "[1.0.0, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", + "Shouldly": "[4.2.1, )", + "System.Linq.Async": "[6.0.1, )", + "Testcontainers.MongoDb": "[3.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.6.5, )" + } + }, + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.mongodb": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.provisioner": { + "type": "Project", + "dependencies": { + "EntityDb.MongoDb": "[1.0.0, )", + "System.CommandLine": "[2.0.0-beta4.22272.1, )", + "System.CommandLine.NamingConventionBinder": "[2.0.0-beta4.22272.1, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.redis": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "EntityDb.Json": "[1.0.0, )", + "StackExchange.Redis": "[2.7.10, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "Bogus": { + "type": "Direct", + "requested": "[35.3.0, )", + "resolved": "35.3.0", + "contentHash": "A+SV1AZQR+cuKLaq4GifqGWl+E85q/9ewHCXqtdNZIH0NZIHr5wrlKOS2dcp94eXKxY9DJpgwRKdPu9pWqV9dw==" + }, + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", + "dependencies": { + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", + "dependencies": { + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" + } + }, + "Xunit.DependencyInjection": { + "type": "Direct", + "requested": "[8.9.1, )", + "resolved": "8.9.1", + "contentHash": "Y+198NdYYTLO52MXraBI+8YQq1ej56FE49JqB8nh0wiFVBB68i3XmGWif6SUCo5v2AkNkX9Kt5v7V+ALbvAlag==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Hosting": "6.0.0", + "xunit.extensibility.execution": "[2.4.2, 3.0.0)" + } + }, + "Xunit.DependencyInjection.Logging": { + "type": "Direct", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "K4w1q20NZD/HiLWdot9KO4sGCkwNw2v4oq/Rn2F1YPjFnxWnmFGjJTpb5fyPESGpwExdDROZqotHQ4TrHMcj5A==", + "dependencies": { + "MartinCostello.Logging.XUnit": "0.3.0", + "Xunit.DependencyInjection": "8.7.1" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.6, )", + "resolved": "2.5.6", + "contentHash": "CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "gnEgxBlk4PFEfdPE8Lkf4+D16MZFYSaW7/o6Wwe5e035QWUkTJX0Dn4LfTCdV5QSEL/fOFxu+yCAm55eIIBgog==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.100.14", + "contentHash": "dGCVuVo0CFUKWW85W8YENO+aREf8sCBDjvGbnNvxJuNW4Ss+brEU9ltHhq2KfZze2VUNK1/wygbPG1bmbpyXEw==", + "dependencies": { + "AWSSDK.Core": "[3.7.100.14, 4.0.0)" + } + }, + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Docker.DotNet": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.1", + "System.Buffers": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Docker.DotNet.X509": { + "type": "Transitive", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", + "dependencies": { + "Docker.DotNet": "3.125.15" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "MartinCostello.Logging.XUnit": { + "type": "Transitive", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "tq2wXyh3fL17EMF2bXgRhU7JrbO3on93MRKYxzz4JzzvuGSA1l0W3GI9/tl8EO89TH+KWEymP7bcFway6z9fXg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", "dependencies": { - "NETStandard.Library": "1.6.1" + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" } }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "DjYkzqvhiHCq38LW71PcIxXk6nhtV6VySP9yDcSO0goPl7YCU1VG1f2Wbgy58lkA10pWkjHCblZPUyboCB93ZA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lB0Hb2V4+RUHy+LjEcqEr4EcV4RWc9EnjAV2GdtWQEdljQX+R4hGREftI7sInU9okP93pDrJiaj6QUJ6ZsslOA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M8VzD0ni5VarIRT8njnwK4K2WSAo0kZH4Zc3mKcSGkP4CjDZ91T9ZEFmmwhmo4z7x8AFq+tW0WFi9wX+K2cxkQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.0", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Logging.Console": "6.0.0", + "Microsoft.Extensions.Logging.Debug": "6.0.0", + "Microsoft.Extensions.Logging.EventLog": "6.0.0", + "Microsoft.Extensions.Logging.EventSource": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", + "dependencies": { + "NuGet.Frameworks": "6.5.0", + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.8.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "IX9tycM35xK5hFwnU+rzharPJOtKYtON6E6Lp2nwOVjh40TUcS/HYToEEWZkLgqKNMCfYPK3Fz3QUCxzhkQRGA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.23.1", + "contentHash": "K8LMdnVgT82vdbSllv8VzjPOLa9k5rLcCBd1fG45z+QGJNPWzAFW5lLgLJQ7xXuJgQIwvP1DBx6X6ecWBtox7g==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", + "dependencies": { + "System.IO.Pipelines": "5.0.1" + } + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "SharpZipLib": { + "type": "Transitive", + "resolved": "1.4.2", + "contentHash": "yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "SSH.NET": { + "type": "Transitive", + "resolved": "2023.0.0", + "contentHash": "g+3VDUrYhm0sqSxmlQFgRFrmBxhQvVh4pfn4pqjkX7WXE3tTjt1tIsOtjuz3mz/5s8gFFQVRydwCJ7Ohs54sJA==", + "dependencies": { + "SshNet.Security.Cryptography": "[1.3.0]" + } + }, + "SshNet.Security.Cryptography": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Concurrent": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.CommandLine": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" + }, + "System.CommandLine.NamingConventionBinder": { + "type": "Transitive", + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "ux2eUA/syF+JtlpMDc/Lsd6PBIBuwjH3AvHnestoh5uD0WKT5b+wkQxDWVCqp9qgVjMBTLNhX19ZYFtenunt9A==", + "dependencies": { + "System.CommandLine": "2.0.0-beta4.22272.1" + } + }, + "System.Console": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.Tracing": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Calendars": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + } + }, + "System.IO.Compression.ZipFile": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.IO.FileSystem": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.FileSystem.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Net.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Net.Sockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Runtime.Numerics": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Security.Cryptography.Csp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encoding.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "6.0.9", + "contentHash": "2j16oUgtIzl7Xtk7demG0i/v5aU/ZvULcAnJvPb63U3ZhXJ494UYcxuEj5Fs49i3XDrk5kU/8I+6l9zRCw3cJw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Threading.Timer": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "Testcontainers": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Nn9/mVOiYEC1mdE0Kr2xQfVAV9mssLedalC6OnC59kHOudx2IhVgenHc983LIdMIhsYF9ywVnyW7HrJE7qVuJg==", + "dependencies": { + "BouncyCastle.Cryptography": "2.2.1", + "Docker.DotNet": "3.125.15", + "Docker.DotNet.X509": "3.125.15", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.4", + "SSH.NET": "2023.0.0", + "SharpZipLib": "1.4.2", + "System.Text.Json": "6.0.9" + } + }, + "Testcontainers.MongoDb": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "Testcontainers.Redis": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" + }, "xunit.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "dnV0Mn2s1C0y2m33AylQyMkEyhBQsL4R0302kwSGiEGuY3JwzEmhTa9pnghyMRPliYSs4fXfkEAP+5bKXryGFg==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.5.0]", - "xunit.extensibility.execution": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "xRm6NIV3i7I+LkjsAJ91Xz2fxJm/oMEi2CYq1G5HlGTgcK1Zo2wNbLO6nKX1VG5FZzXibSdoLwr/MofVvh3mFA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1587,17 +3260,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.5.0", - "contentHash": "7+v2Bvp+1ew1iMGQVb1glICi8jcNdHbRUX6Ru0dmJBViGdjiS7kyqcX2VxleQhFbKNi+WF0an7/TeTXD283RlQ==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.5.0]" + "xunit.extensibility.core": "[2.6.5]" } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "entitydb.abstractions": { "type": "Project", @@ -1615,19 +3288,19 @@ "entitydb.common.tests": { "type": "Project", "dependencies": { - "Bogus": "[34.0.2, )", + "Bogus": "[35.3.0, )", "EntityDb.Common": "[1.0.0, )", "EntityDb.Provisioner": "[1.0.0, )", "EntityDb.Redis": "[1.0.0, )", - "Microsoft.NET.Test.Sdk": "[17.7.1, )", - "Moq": "[4.18.2, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", "Shouldly": "[4.2.1, )", "System.Linq.Async": "[6.0.1, )", - "Testcontainers.MongoDb": "[3.0.0, )", - "Testcontainers.Redis": "[3.0.0, )", - "Xunit.DependencyInjection": "[8.8.2, )", + "Testcontainers.MongoDb": "[3.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", "Xunit.DependencyInjection.Logging": "[8.1.0, )", - "xunit": "[2.5.0, )" + "xunit": "[2.6.5, )" } }, "entitydb.json": { @@ -1641,7 +3314,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.21.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1659,7 +3332,7 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.122, )", + "StackExchange.Redis": "[2.7.10, )", "System.Linq.Async": "[6.0.1, )" } } From 4831091ea03ed95730f89002892656e93a3dbd62 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Wed, 10 Jan 2024 00:51:55 -0800 Subject: [PATCH 73/93] refactor: better object names + entity and stream repos will short-circuit returns true if there are no messages to commit --- .../Entities/IMultipleEntityRepository.cs | 2 +- .../Entities/ISingleEntityRepository.cs | 2 +- .../Projections/IProjection.cs | 4 +- .../Sources/ISourceRepository.cs | 86 ++++++------ src/EntityDb.Abstractions/Sources/Message.cs | 4 +- .../Queries/FilterBuilders/IFilterBuilder.cs | 2 +- .../IMessageDataFilterBuilder.cs | 2 +- .../Queries/{IQuery.cs => IDataQuery.cs} | 4 +- ...{ILeaseQuery.cs => ILeaseDataDataQuery.cs} | 4 +- ...eDataQuery.cs => IMessageDataDataQuery.cs} | 2 +- ...ceDataQuery.cs => ISourceDataDataQuery.cs} | 2 +- .../{ITagQuery.cs => ITagDataDataQuery.cs} | 4 +- ...ortBuilder.cs => ILeaseDataSortBuilder.cs} | 2 +- .../Queries/SortBuilders/ISortBuilder.cs | 2 +- ...gSortBuilder.cs => ITagDataSortBuilder.cs} | 2 +- .../Streams/IMultipleStreamRepository.cs | 2 +- .../Streams/ISingleStreamRepository.cs | 2 +- .../Entities/MultipleEntityRepository.cs | 7 +- ...Query.cs => ModifiedLeaseDataDataQuery.cs} | 14 +- ...ery.cs => ModifiedMessageDataDataQuery.cs} | 12 +- .../Queries/Modified/ModifiedQueryBase.cs | 8 +- .../Queries/Modified/ModifiedQueryOptions.cs | 8 +- ...uery.cs => ModifiedSourceDataDataQuery.cs} | 12 +- ...agQuery.cs => ModifiedTagDataDataQuery.cs} | 14 +- .../Sources/Queries/QueryExtensions.cs | 44 +++--- .../ReverseLeaseDataSortBuilder.cs | 34 +++++ .../SortBuilders/ReverseLeaseSortBuilder.cs | 34 ----- .../SortBuilders/ReverseTagDataSortBuilder.cs | 29 ++++ .../SortBuilders/ReverseTagSortBuilder.cs | 29 ---- .../SortBuilders/SortBuilderExtensions.cs | 20 +-- ...easesQuery.cs => DeleteLeasesDataQuery.cs} | 6 +- ...eteTagsQuery.cs => DeleteTagsDataQuery.cs} | 6 +- ...etDeltasQuery.cs => GetDeltasDataQuery.cs} | 4 +- ...ery.cs => GetLastStateVersionDataQuery.cs} | 2 +- ...etSourceQuery.cs => GetSourceDataQuery.cs} | 2 +- ...easeQuery.cs => MatchingLeaseDataQuery.cs} | 4 +- ...sesQuery.cs => MatchingLeasesDataQuery.cs} | 4 +- .../BufferBlockSourceReprocessorQueue.cs | 2 +- .../ISourceReprocessorQueueItem.cs | 2 +- .../TestModeSourceReprocessorQueue.cs | 2 +- .../Sources/SourceRepositoryExtensions.cs | 18 +-- .../Sources/SourceRepositoryWrapper.cs | 56 ++++---- .../Streams/MultipleStreamRepository.cs | 7 +- .../Documents/AgentSignatureDocument.cs | 12 +- .../Documents/DeltaDataDocument.cs | 14 +- .../Documents/LeaseDataDocument.cs | 16 +-- .../Documents/TagDataDocument.cs | 16 +-- .../Sources/MongoDbSourceRepository.cs | 56 ++++---- ...SortBuilder.cs => LeaseDataSortBuilder.cs} | 2 +- ...agSortBuilder.cs => TagDataSortBuilder.cs} | 2 +- .../Sessions/MongoDbSourceSessionOptions.cs | 2 +- .../Entities/EntityTests.cs | 4 +- .../Projections/OneToOneProjection.cs | 2 +- ...Query.cs => CountDataDataDataDataQuery.cs} | 6 +- ...ry.cs => SourceIdDataDataDataDataQuery.cs} | 8 +- ...> SourceTimeStampDataDataDataDataQuery.cs} | 10 +- ...Query.cs => StateDataDataDataDataQuery.cs} | 8 +- ...s => StateVersionDataDataDataDataQuery.cs} | 8 +- .../Sources/SourceTests.cs | 130 +++++++++--------- .../Sources/TryCatchSourceRepositoryTests.cs | 60 ++++---- .../Streams/StreamTests.cs | 16 +-- test/EntityDb.Common.Tests/TestsBase.cs | 2 +- 62 files changed, 446 insertions(+), 436 deletions(-) rename src/EntityDb.Abstractions/Sources/Queries/{IQuery.cs => IDataQuery.cs} (81%) rename src/EntityDb.Abstractions/Sources/Queries/{ILeaseQuery.cs => ILeaseDataDataQuery.cs} (88%) rename src/EntityDb.Abstractions/Sources/Queries/{IMessageDataQuery.cs => IMessageDataDataQuery.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/{ISourceDataQuery.cs => ISourceDataDataQuery.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/{ITagQuery.cs => ITagDataDataQuery.cs} (88%) rename src/EntityDb.Abstractions/Sources/Queries/SortBuilders/{ILeaseSortBuilder.cs => ILeaseDataSortBuilder.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/SortBuilders/{ITagSortBuilder.cs => ITagDataSortBuilder.cs} (91%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedLeaseQuery.cs => ModifiedLeaseDataDataQuery.cs} (52%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedMessageDataQuery.cs => ModifiedMessageDataDataQuery.cs} (58%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedSourceDataQuery.cs => ModifiedSourceDataDataQuery.cs} (58%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedTagQuery.cs => ModifiedTagDataDataQuery.cs} (52%) create mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs delete mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs create mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs delete mode 100644 src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs rename src/EntityDb.Common/Sources/Queries/Standard/{DeleteLeasesQuery.cs => DeleteLeasesDataQuery.cs} (77%) rename src/EntityDb.Common/Sources/Queries/Standard/{DeleteTagsQuery.cs => DeleteTagsDataQuery.cs} (84%) rename src/EntityDb.Common/Sources/Queries/Standard/{GetDeltasQuery.cs => GetDeltasDataQuery.cs} (84%) rename src/EntityDb.Common/Sources/Queries/Standard/{GetLastStateVersionQuery.cs => GetLastStateVersionDataQuery.cs} (81%) rename src/EntityDb.Common/Sources/Queries/Standard/{GetSourceQuery.cs => GetSourceDataQuery.cs} (89%) rename src/EntityDb.Common/Sources/Queries/Standard/{MatchingLeaseQuery.cs => MatchingLeaseDataQuery.cs} (78%) rename src/EntityDb.Common/Sources/Queries/Standard/{MatchingLeasesQuery.cs => MatchingLeasesDataQuery.cs} (83%) rename src/EntityDb.MongoDb/Sources/Queries/SortBuilders/{LeaseSortBuilder.cs => LeaseDataSortBuilder.cs} (81%) rename src/EntityDb.MongoDb/Sources/Queries/SortBuilders/{TagSortBuilder.cs => TagDataSortBuilder.cs} (78%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{CountQuery.cs => CountDataDataDataDataQuery.cs} (84%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{SourceIdQuery.cs => SourceIdDataDataDataDataQuery.cs} (84%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{SourceTimeStampQuery.cs => SourceTimeStampDataDataDataDataQuery.cs} (85%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{StateQuery.cs => StateDataDataDataDataQuery.cs} (83%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{StateVersionQuery.cs => StateVersionDataDataDataDataQuery.cs} (80%) diff --git a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs index ef7512b1..87b4cd03 100644 --- a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs @@ -60,6 +60,6 @@ public interface IMultipleEntityRepository : IDisposableResource /// Atomically commits a source. /// /// A cancellation token. - /// true if the commit succeeded, or false if the commit failed. + /// Only returns false if there are uncommitted messages. Task Commit(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs index eb3910fc..b3efef1f 100644 --- a/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs @@ -42,6 +42,6 @@ public interface ISingleEntityRepository : IDisposableResource /// Atomically commits a source. /// /// A cancellation token. - /// true if the commit succeeded, or false if the commit failed. + /// Only returns false if there are uncommitted messages. Task Commit(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index 47c016b4..1ddbf4fa 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -18,13 +18,13 @@ public interface IProjection : IState void Mutate(Source source); /// - /// Returns a that finds sources that need to be passed to the reducer. + /// Returns a that finds sources that need to be passed to the reducer. /// /// A service provider for fetching repositories. /// A pointer to the desired projection state /// A cancellation token /// - /// A that is used to load the rest of the messages for the given projection + /// A that is used to load the rest of the messages for the given projection /// pointer. /// IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Pointer projectionPointer, diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs index 9cec7a05..6f0ea530 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -14,128 +14,128 @@ public interface ISourceRepository : IDisposableResource /// /// Returns the source ids which are found by a message group query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a lease query. /// - /// The lease query. + /// The lease query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a tag query. /// - /// The tag query. + /// The tag query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a agentSignature query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a lease query. /// - /// The lease query. + /// The lease query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ILeaseQuery leaseQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a tag query. /// - /// The tag query. + /// The tag query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ITagQuery tagQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the agentSignatures which are found by an message group query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The agent signatures which are found by . - IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, + /// The agent signatures which are found by . + IAsyncEnumerable EnumerateAgentSignatures(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the deltas which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The deltas which are found by . - IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, + /// The deltas which are found by . + IAsyncEnumerable EnumerateDeltas(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the leases which are found by a lease query. /// - /// The lease query. + /// The lease query. /// A cancellation token. - /// The leases which are found by . - IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, + /// The leases which are found by . + IAsyncEnumerable EnumerateLeases(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the tags which are found by a tag query. /// - /// The tag query. + /// The tag query. /// A cancellation token. - /// The tags which are found by . - IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, + /// The tags which are found by . + IAsyncEnumerable EnumerateTags(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the annotated agent signatures which are found by a message group query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The annotated agent signatures which are found by . + /// The annotated agent signatures which are found by . IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - ISourceDataQuery sourceDataQuery, + ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default); /// /// Returns the annotated deltas which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The annotated deltas which are found by . - IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, + /// The annotated deltas which are found by . + IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default); /// @@ -143,6 +143,6 @@ IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessag /// /// The source. /// A cancellation token. - /// true if the insert succeeded, or false if the insert failed. + /// Returns true unless the source cannot be committed. Task Commit(Source source, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index c61de1eb..aacf43dd 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -34,12 +34,12 @@ public sealed record Message public ITag[] AddTags { get; init; } = Array.Empty(); /// - /// The tags to be deleted. + /// The leases to be deleted. /// public ILease[] DeleteLeases { get; init; } = Array.Empty(); /// - /// The aliases to be added. + /// The tags to be deleted. /// public ITag[] DeleteTags { get; init; } = Array.Empty(); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs index f9190f59..01297af5 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs @@ -4,7 +4,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// /// Builds a for an object. Possible objects include: -/// agent signatures, deltas, facts, tags, and aliases +/// agent signatures, deltas, facts, and tags. /// /// The type of filter used by the repository. public interface IFilterBuilder diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs index 5a01e242..c1a144f4 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// /// Builds a for an object associated /// with a single message. Possible objects include: -/// deltas, facts, tags, and aliases +/// deltas, facts, and tags. /// /// The type of filter used by the repository. public interface IMessageDataFilterBuilder : IFilterBuilder diff --git a/src/EntityDb.Abstractions/Sources/Queries/IQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IDataQuery.cs similarity index 81% rename from src/EntityDb.Abstractions/Sources/Queries/IQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IDataQuery.cs index 7d1691f4..9fa5fb9e 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/IQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IDataQuery.cs @@ -2,9 +2,9 @@ /// /// Abstracts a query for an object repository. Possible objects include: -/// agentSignatures, deltas, facts, leases, and aliases. +/// agentSignatures, deltas, facts, and leases. /// -public interface IQuery +public interface IDataQuery { /// /// The number of objects to skip. diff --git a/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataDataQuery.cs similarity index 88% rename from src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ILeaseDataDataQuery.cs index c0805d72..7ae12f03 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ILeaseQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on leases. /// -public interface ILeaseQuery : IQuery +public interface ILeaseDataDataQuery : IDataQuery { /// /// Returns a built from a lease filter builder. @@ -22,5 +22,5 @@ public interface ILeaseQuery : IQuery /// The type of sort used by the repository. /// The lease sort builder. /// A built from . - TSort? GetSort(ILeaseSortBuilder builder); + TSort? GetSort(ILeaseDataSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataDataQuery.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IMessageDataDataQuery.cs index 3002f6cc..f5879451 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on messages. /// -public interface IMessageDataQuery : IQuery +public interface IMessageDataDataQuery : IDataQuery { /// /// Returns a built from a message filter builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataDataQuery.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ISourceDataDataQuery.cs index 397dab7f..f29bbd78 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on agentSignatures. /// -public interface ISourceDataQuery : IQuery +public interface ISourceDataDataQuery : IDataQuery { /// /// Returns a built from a agentSignature filter builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ITagDataDataQuery.cs similarity index 88% rename from src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ITagDataDataQuery.cs index 7cd5c5c4..40bc64a7 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ITagQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ITagDataDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on tags. /// -public interface ITagQuery : IQuery +public interface ITagDataDataQuery : IDataQuery { /// /// Returns a built from a tag filter builder. @@ -22,5 +22,5 @@ public interface ITagQuery : IQuery /// The type of sort used by the repository. /// The tag sort builder. /// A built from . - TSort? GetSort(ITagSortBuilder builder); + TSort? GetSort(ITagDataSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs index ba5b161e..7f2bc14a 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; /// Builds a sort for a lease query. /// /// The type of sort used by the repository. -public interface ILeaseSortBuilder : IMessageDataSortBuilder +public interface ILeaseDataSortBuilder : IMessageDataSortBuilder { /// /// Returns a that orders leases by . diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs index 38653ab4..6feae84c 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs @@ -2,7 +2,7 @@ /// /// Builds a sort for an object repository. Possible objects include: -/// agentSignatures, deltas, facts, leases, and aliases. +/// agentSignatures, deltas, facts, and leases. /// /// The type of sort used by the repository. public interface ISortBuilder diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs similarity index 91% rename from src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs index 292defab..d160cccc 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; /// Builds a sort for a tag query. /// /// The type of sort used by the repository. -public interface ITagSortBuilder : IMessageDataSortBuilder +public interface ITagDataSortBuilder : IMessageDataSortBuilder { /// /// Returns a that orders tags by . diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs index 75d41d64..8119452c 100644 --- a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -36,6 +36,6 @@ public interface IMultipleStreamRepository : IDisposableResource /// Commits all stages deltas. /// /// A cancellation token - /// true if the commit succeeded, or false if the commit failed. + /// Only returns false if there are uncommitted messages. Task Commit(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs index 8be8471e..7c23c36b 100644 --- a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs @@ -32,6 +32,6 @@ public interface ISingleStreamRepository : IDisposableResource /// Commits all stages deltas. /// /// A cancellation token - /// true if the commit succeeded, or false if the commit failed. + /// Only returns false if there are uncommitted messages. Task Commit(CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index 23cedcd2..242f1ea5 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -60,7 +60,7 @@ public async Task Load(Pointer entityPointer, CancellationToken cancellationToke var statePointer = state.GetPointer(); - var query = new GetDeltasQuery(entityPointer, statePointer.Version); + var query = new GetDeltasDataQuery(entityPointer, statePointer.Version); var entity = await SourceRepository .EnumerateDeltas(query, cancellationToken) @@ -122,6 +122,11 @@ public void Append(Id entityId, object delta) public async Task Commit(CancellationToken cancellationToken = default) { + if (_messages.Count == 0) + { + return true; + } + var source = new Source { Id = Id.NewId(), diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataDataQuery.cs similarity index 52% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataDataQuery.cs index 4512aa35..d6812239 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedLeaseQuery : ModifiedQueryBase, ILeaseQuery +internal sealed record ModifiedLeaseDataDataQuery : ModifiedQueryBase, ILeaseDataDataQuery { - public required ILeaseQuery LeaseQuery { get; init; } - protected override IQuery Query => LeaseQuery; + public required ILeaseDataDataQuery LeaseDataDataQuery { get; init; } + protected override IDataQuery DataQuery => LeaseDataDataQuery; public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.Not ( - LeaseQuery.GetFilter(builder) + LeaseDataDataQuery.GetFilter(builder) ); } - return LeaseQuery.GetFilter(builder); + return LeaseDataDataQuery.GetFilter(builder); } - public TSort? GetSort(ILeaseSortBuilder builder) + public TSort? GetSort(ILeaseDataSortBuilder builder) { - return LeaseQuery.GetSort(ModifiedQueryOptions.ReverseSort + return LeaseDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataDataQuery.cs similarity index 58% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataDataQuery.cs index c7889888..ca7b0831 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedMessageDataQuery : ModifiedQueryBase, IMessageDataQuery +internal sealed record ModifiedMessageDataDataQuery : ModifiedQueryBase, IMessageDataDataQuery { - public required IMessageDataQuery MessageDataQuery { get; init; } - protected override IQuery Query => MessageDataQuery; + public required IMessageDataDataQuery MessageDataDataQuery { get; init; } + protected override IDataQuery DataQuery => MessageDataDataQuery; public TFilter GetFilter(IMessageDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(IMessageDataFilterBuilder builder) { return builder.Not ( - MessageDataQuery.GetFilter(builder) + MessageDataDataQuery.GetFilter(builder) ); } - return MessageDataQuery.GetFilter(builder); + return MessageDataDataQuery.GetFilter(builder); } public TSort? GetSort(IMessageDataSortBuilder builder) { - return MessageDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + return MessageDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs index 60bbaaf4..3df744d2 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs @@ -4,12 +4,12 @@ namespace EntityDb.Common.Sources.Queries.Modified; internal abstract record ModifiedQueryBase { - protected abstract IQuery Query { get; } + protected abstract IDataQuery DataQuery { get; } public required ModifiedQueryOptions ModifiedQueryOptions { get; init; } - public int? Skip => ModifiedQueryOptions.ReplaceSkip ?? Query.Skip; + public int? Skip => ModifiedQueryOptions.ReplaceSkip ?? DataQuery.Skip; - public int? Take => ModifiedQueryOptions.ReplaceTake ?? Query.Take; + public int? Take => ModifiedQueryOptions.ReplaceTake ?? DataQuery.Take; - public object? Options => Query.Options; + public object? Options => DataQuery.Options; } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs index 2da67dfe..701382be 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs @@ -23,14 +23,14 @@ public record ModifiedQueryOptions public bool ReverseSort { get; init; } /// - /// If not null, then the new query will return this value for . Otherwise, the new - /// query will return the same as the original query. + /// If not null, then the new query will return this value for . Otherwise, the new + /// query will return the same as the original query. /// public int? ReplaceSkip { get; init; } /// - /// If not null, then the new query will return this value for . Otherwise, the new - /// query will return the same as the original query. + /// If not null, then the new query will return this value for . Otherwise, the new + /// query will return the same as the original query. /// public int? ReplaceTake { get; init; } } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataDataQuery.cs similarity index 58% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataDataQuery.cs index 3c2c2522..51e8d362 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedSourceDataQuery : ModifiedQueryBase, ISourceDataQuery +internal sealed record ModifiedSourceDataDataQuery : ModifiedQueryBase, ISourceDataDataQuery { - public required ISourceDataQuery SourceDataQuery { get; init; } - protected override IQuery Query => SourceDataQuery; + public required ISourceDataDataQuery SourceDataDataQuery { get; init; } + protected override IDataQuery DataQuery => SourceDataDataQuery; public TFilter GetFilter(ISourceDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(ISourceDataFilterBuilder builder) { return builder.Not ( - SourceDataQuery.GetFilter(builder) + SourceDataDataQuery.GetFilter(builder) ); } - return SourceDataQuery.GetFilter(builder); + return SourceDataDataQuery.GetFilter(builder); } public TSort? GetSort(ISourceDataSortBuilder builder) { - return SourceDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + return SourceDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataDataQuery.cs similarity index 52% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataDataQuery.cs index 49b003e6..e5c45e9e 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedTagQuery : ModifiedQueryBase, ITagQuery +internal sealed record ModifiedTagDataDataQuery : ModifiedQueryBase, ITagDataDataQuery { - public required ITagQuery TagQuery { get; init; } - protected override IQuery Query => TagQuery; + public required ITagDataDataQuery TagDataDataQuery { get; init; } + protected override IDataQuery DataQuery => TagDataDataQuery; public TFilter GetFilter(ITagDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.Not ( - TagQuery.GetFilter(builder) + TagDataDataQuery.GetFilter(builder) ); } - return TagQuery.GetFilter(builder); + return TagDataDataQuery.GetFilter(builder); } - public TSort? GetSort(ITagSortBuilder builder) + public TSort? GetSort(ITagDataSortBuilder builder) { - return TagQuery.GetSort(ModifiedQueryOptions.ReverseSort + return TagDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs index 3934cd7a..848cb554 100644 --- a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs @@ -9,59 +9,59 @@ namespace EntityDb.Common.Sources.Queries; public static class QueryExtensions { /// - /// Returns a new, modified . The way in which + /// Returns a new, modified . The way in which /// it is modified depends on the parameters of this extension method. /// - /// The source data query. + /// The source data query. /// The options for modifying the query. - /// A new, modified . - public static ISourceDataQuery Modify(this ISourceDataQuery sourceDataQuery, + /// A new, modified . + public static ISourceDataDataQuery Modify(this ISourceDataDataQuery sourceDataDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedSourceDataQuery + return new ModifiedSourceDataDataQuery { - ModifiedQueryOptions = modifiedQueryOptions, SourceDataQuery = sourceDataQuery, + ModifiedQueryOptions = modifiedQueryOptions, SourceDataDataQuery = sourceDataDataQuery, }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters + /// Returns a new, modified . The way in which it is modified depends on the parameters /// of /// this extension method. /// - /// The message data query. + /// The message data query. /// The options for modifying the query. - /// A new, modified . - public static IMessageDataQuery Modify(this IMessageDataQuery messageDataQuery, + /// A new, modified . + public static IMessageDataDataQuery Modify(this IMessageDataDataQuery messageDataDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedMessageDataQuery + return new ModifiedMessageDataDataQuery { - ModifiedQueryOptions = modifiedQueryOptions, MessageDataQuery = messageDataQuery, + ModifiedQueryOptions = modifiedQueryOptions, MessageDataDataQuery = messageDataDataQuery, }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// Returns a new, modified . The way in which it is modified depends on the parameters of /// this extension method. /// - /// The lease query. + /// The lease query. /// The options for modifying the query. - /// A new, modified . - public static ILeaseQuery Modify(this ILeaseQuery leaseQuery, ModifiedQueryOptions modifiedQueryOptions) + /// A new, modified . + public static ILeaseDataDataQuery Modify(this ILeaseDataDataQuery leaseDataDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedLeaseQuery { ModifiedQueryOptions = modifiedQueryOptions, LeaseQuery = leaseQuery }; + return new ModifiedLeaseDataDataQuery { ModifiedQueryOptions = modifiedQueryOptions, LeaseDataDataQuery = leaseDataDataQuery }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of this + /// Returns a new, modified . The way in which it is modified depends on the parameters of this /// extension method. /// - /// The tag query. + /// The tag query. /// The options for modifying the query. - /// A new, modified . - public static ITagQuery Modify(this ITagQuery tagQuery, ModifiedQueryOptions modifiedQueryOptions) + /// A new, modified . + public static ITagDataDataQuery Modify(this ITagDataDataQuery tagDataDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedTagQuery { ModifiedQueryOptions = modifiedQueryOptions, TagQuery = tagQuery }; + return new ModifiedTagDataDataQuery { ModifiedQueryOptions = modifiedQueryOptions, TagDataDataQuery = tagDataDataQuery }; } } diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs new file mode 100644 index 00000000..ddf62810 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs @@ -0,0 +1,34 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal sealed record ReverseLeaseDataSortBuilder : ReverseSortBuilderBase, ILeaseDataSortBuilder +{ + public required ILeaseDataSortBuilder LeaseDataSortBuilder { get; init; } + protected override ISortBuilder SortBuilder => LeaseDataSortBuilder; + + public TSort StateId(bool ascending) + { + return LeaseDataSortBuilder.StateId(!ascending); + } + + public TSort StateVersion(bool ascending) + { + return LeaseDataSortBuilder.StateVersion(!ascending); + } + + public TSort LeaseScope(bool ascending) + { + return LeaseDataSortBuilder.LeaseScope(!ascending); + } + + public TSort LeaseLabel(bool ascending) + { + return LeaseDataSortBuilder.LeaseLabel(!ascending); + } + + public TSort LeaseValue(bool ascending) + { + return LeaseDataSortBuilder.LeaseValue(!ascending); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs deleted file mode 100644 index 68bc45de..00000000 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseSortBuilder.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.SortBuilders; - -namespace EntityDb.Common.Sources.Queries.SortBuilders; - -internal sealed record ReverseLeaseSortBuilder : ReverseSortBuilderBase, ILeaseSortBuilder -{ - public required ILeaseSortBuilder LeaseSortBuilder { get; init; } - protected override ISortBuilder SortBuilder => LeaseSortBuilder; - - public TSort StateId(bool ascending) - { - return LeaseSortBuilder.StateId(!ascending); - } - - public TSort StateVersion(bool ascending) - { - return LeaseSortBuilder.StateVersion(!ascending); - } - - public TSort LeaseScope(bool ascending) - { - return LeaseSortBuilder.LeaseScope(!ascending); - } - - public TSort LeaseLabel(bool ascending) - { - return LeaseSortBuilder.LeaseLabel(!ascending); - } - - public TSort LeaseValue(bool ascending) - { - return LeaseSortBuilder.LeaseValue(!ascending); - } -} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs new file mode 100644 index 00000000..f5ca19ef --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs @@ -0,0 +1,29 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal sealed record ReverseTagDataSortBuilder : ReverseSortBuilderBase, ITagDataSortBuilder +{ + public required ITagDataSortBuilder TagDataSortBuilder { get; init; } + protected override ISortBuilder SortBuilder => TagDataSortBuilder; + + public TSort StateId(bool ascending) + { + return TagDataSortBuilder.StateId(!ascending); + } + + public TSort StateVersion(bool ascending) + { + return TagDataSortBuilder.StateVersion(!ascending); + } + + public TSort TagLabel(bool ascending) + { + return TagDataSortBuilder.TagLabel(!ascending); + } + + public TSort TagValue(bool ascending) + { + return TagDataSortBuilder.TagValue(!ascending); + } +} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs deleted file mode 100644 index a13a1f5e..00000000 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagSortBuilder.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EntityDb.Abstractions.Sources.Queries.SortBuilders; - -namespace EntityDb.Common.Sources.Queries.SortBuilders; - -internal sealed record ReverseTagSortBuilder : ReverseSortBuilderBase, ITagSortBuilder -{ - public required ITagSortBuilder TagSortBuilder { get; init; } - protected override ISortBuilder SortBuilder => TagSortBuilder; - - public TSort StateId(bool ascending) - { - return TagSortBuilder.StateId(!ascending); - } - - public TSort StateVersion(bool ascending) - { - return TagSortBuilder.StateVersion(!ascending); - } - - public TSort TagLabel(bool ascending) - { - return TagSortBuilder.TagLabel(!ascending); - } - - public TSort TagValue(bool ascending) - { - return TagSortBuilder.TagValue(!ascending); - } -} diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs index 09022e6f..c80ba5f2 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs @@ -38,32 +38,32 @@ public static IMessageDataSortBuilder Reverse(this IMessageDataSor } /// - /// Returns a that orders leases in the reverse order of another - /// . + /// Returns a that orders leases in the reverse order of another + /// . /// /// The type of sort used by the repository. /// The lease sort builder. /// - /// A that orders leases in the reverse order of + /// A that orders leases in the reverse order of /// . /// - public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder builder) + public static ILeaseDataSortBuilder Reverse(this ILeaseDataSortBuilder builder) { - return new ReverseLeaseSortBuilder { LeaseSortBuilder = builder }; + return new ReverseLeaseDataSortBuilder { LeaseDataSortBuilder = builder }; } /// - /// Returns a that orders tags in the reverse order of another - /// . + /// Returns a that orders tags in the reverse order of another + /// . /// /// The type of sort used by the repository. /// The tag sort builder. /// - /// A that orders tags in the reverse order of + /// A that orders tags in the reverse order of /// . /// - public static ITagSortBuilder Reverse(this ITagSortBuilder builder) + public static ITagDataSortBuilder Reverse(this ITagDataSortBuilder builder) { - return new ReverseTagSortBuilder { TagSortBuilder = builder }; + return new ReverseTagDataSortBuilder { TagDataSortBuilder = builder }; } } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs similarity index 77% rename from src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs index 2678317e..71a4af75 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs @@ -5,8 +5,8 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record DeleteLeasesQuery(IReadOnlyCollection Leases, - object? Options = null) : ILeaseQuery +internal sealed record DeleteLeasesDataQuery(IReadOnlyCollection Leases, + object? Options = null) : ILeaseDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -23,7 +23,7 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) ); } - public TSort? GetSort(ILeaseSortBuilder builder) + public TSort? GetSort(ILeaseDataSortBuilder builder) { return default; } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs similarity index 84% rename from src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs index 3749ca9c..6f44562f 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs @@ -6,8 +6,8 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record DeleteTagsQuery - (Id StateId, IReadOnlyCollection Tags, object? Options = null) : ITagQuery +internal sealed record DeleteTagsDataQuery + (Id StateId, IReadOnlyCollection Tags, object? Options = null) : ITagDataDataQuery { public TFilter GetFilter(ITagDataFilterBuilder builder) { @@ -26,7 +26,7 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) ); } - public TSort? GetSort(ITagSortBuilder builder) + public TSort? GetSort(ITagDataSortBuilder builder) { return default; } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs similarity index 84% rename from src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs index 1af0442b..8585252d 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs @@ -6,8 +6,8 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetDeltasQuery(Pointer StatePointer, Version PersistedStateVersion, - object? Options = null) : IMessageDataQuery +internal sealed record GetDeltasDataQuery(Pointer StatePointer, Version PersistedStateVersion, + object? Options = null) : IMessageDataDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs similarity index 81% rename from src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs index ac0fd628..0237acde 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetLastStateVersionQuery(Id StateId, object? Options = null) : IMessageDataQuery +internal sealed record GetLastStateVersionDataQuery(Id StateId, object? Options = null) : IMessageDataDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs similarity index 89% rename from src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs index ca36f8ba..835cdae1 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetSourceQuery(Id SourceId) : ISourceDataQuery, IMessageDataQuery +internal sealed record GetSourceDataQuery(Id SourceId) : ISourceDataDataQuery, IMessageDataDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs similarity index 78% rename from src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs index 40aca360..194a97de 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record MatchingLeaseQuery(ILease Lease) : ILeaseQuery +internal sealed record MatchingLeaseDataQuery(ILease Lease) : ILeaseDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -17,7 +17,7 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) ); } - public TSort? GetSort(ILeaseSortBuilder builder) + public TSort? GetSort(ILeaseDataSortBuilder builder) { return default; } diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs similarity index 83% rename from src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs rename to src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs index 3355a99f..611384ea 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record MatchingLeasesQuery(params ILease[] Leases) : ILeaseQuery +internal sealed record MatchingLeasesDataQuery(params ILease[] Leases) : ILeaseDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -21,7 +21,7 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) ); } - public TSort? GetSort(ILeaseSortBuilder builder) + public TSort? GetSort(ILeaseDataSortBuilder builder) { return default; } diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs index eaf10a55..b6dde1d1 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs @@ -49,7 +49,7 @@ await _sourceRepositoryFactory.Create(item.SourceSessionOptionsName, cancellationToken); var sourceIds = await sourceRepository - .EnumerateSourceIds(item.Query, cancellationToken) + .EnumerateSourceIds(item.DataQuery, cancellationToken) .ToArrayAsync(cancellationToken); foreach (var sourceId in sourceIds) diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs index 5a0aa343..92be236e 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/ISourceReprocessorQueueItem.cs @@ -24,7 +24,7 @@ public interface ISourceReprocessorQueueItem /// /// Determines which sources need to be reprocessed. /// - IQuery Query { get; } + IDataQuery DataQuery { get; } /// /// Determines how long to wait between each call to enqueue. diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs index 97ec9dd4..c882b396 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs @@ -34,7 +34,7 @@ await _sourceRepositoryFactory.Create(item.SourceSessionOptionsName, cancellationToken); var sourceIds = await sourceRepository - .EnumerateSourceIds(item.Query, cancellationToken) + .EnumerateSourceIds(item.DataQuery, cancellationToken) .ToArrayAsync(cancellationToken); foreach (var sourceId in sourceIds) diff --git a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs index 2ce7477d..cdd404e4 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs @@ -14,19 +14,19 @@ public static class SourceRepositoryExtensions /// Enumerate source ids for any supported query type /// /// The source repository - /// The query + /// The query /// A cancellation token - /// The source id which are found by + /// The source id which are found by public static IAsyncEnumerable EnumerateSourceIds(this ISourceRepository sourceRepository, - IQuery query, CancellationToken cancellationToken = default) + IDataQuery dataQuery, CancellationToken cancellationToken = default) { - return query switch + return dataQuery switch { - ISourceDataQuery sourceDataQuery => sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken), - IMessageDataQuery messageDataQuery => sourceRepository.EnumerateSourceIds(messageDataQuery, + ISourceDataDataQuery sourceDataQuery => sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken), + IMessageDataDataQuery messageDataQuery => sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken), - ILeaseQuery leaseQuery => sourceRepository.EnumerateSourceIds(leaseQuery, cancellationToken), - ITagQuery tagQuery => sourceRepository.EnumerateSourceIds(tagQuery, cancellationToken), + ILeaseDataDataQuery leaseDataQuery => sourceRepository.EnumerateSourceIds(leaseDataQuery, cancellationToken), + ITagDataDataQuery tagDataQuery => sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken), _ => AsyncEnumerable.Empty(), }; } @@ -63,7 +63,7 @@ public static async Task GetSource CancellationToken cancellationToken = default ) { - var query = new GetSourceQuery(sourceId); + var query = new GetSourceDataQuery(sourceId); var annotatedAgentSignature = await sourceRepository .EnumerateAnnotatedAgentSignatures(query, cancellationToken) diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs index f45f1d5e..a8d8b316 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -16,90 +16,90 @@ protected SourceRepositoryWrapper(ISourceRepository sourceRepository) _sourceRepository = sourceRepository; } - public IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(sourceDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateSourceIds(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateSourceIds(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(leaseQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(leaseDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateSourceIds(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(sourceDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(sourceDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateDeltas(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateLeases(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateLeases(leaseQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateLeases(leaseDataDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateTags(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateTags(tagQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateTags(tagDataDataQuery, cancellationToken)); } public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - ISourceDataQuery sourceDataQuery, + ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => - _sourceRepository.EnumerateAnnotatedAgentSignatures(sourceDataQuery, cancellationToken)); + _sourceRepository.EnumerateAnnotatedAgentSignatures(sourceDataDataQuery, cancellationToken)); } - public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageDataDataQuery, cancellationToken)); } public virtual Task Commit(Source source, diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index df083e0c..98f27c07 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -82,6 +82,11 @@ public async Task Stage(Key streamKey, Key messageKey, object delta, public async Task Commit(CancellationToken cancellationToken = default) { + if (_messages.Count == 0) + { + return true; + } + var source = new Source { Id = Id.NewId(), @@ -119,7 +124,7 @@ public static ILease GetMessageKeyLease(Key streamKey, Key messageKey) private async Task GetStreamPointer(ILease lease, CancellationToken cancellationToken) { - var query = new MatchingLeaseQuery(lease); + var query = new MatchingLeaseDataQuery(lease); return await SourceRepository .EnumerateStatePointers(query, cancellationToken) diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 21ec9d34..0b84c85b 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -57,17 +57,17 @@ Source source public static DocumentQuery GetQuery ( - ISourceDataQuery sourceDataQuery + ISourceDataDataQuery sourceDataDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = sourceDataQuery.GetFilter(FilterBuilder), - Sort = sourceDataQuery.GetSort(SortBuilder), - Skip = sourceDataQuery.Skip, - Limit = sourceDataQuery.Take, - Options = sourceDataQuery.Options as MongoDbQueryOptions, + Filter = sourceDataDataQuery.GetFilter(FilterBuilder), + Sort = sourceDataDataQuery.GetSort(SortBuilder), + Skip = sourceDataDataQuery.Skip, + Limit = sourceDataDataQuery.Take, + Options = sourceDataDataQuery.Options as MongoDbQueryOptions, }; } } diff --git a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs index 185d96f4..de1d2ee3 100644 --- a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs @@ -61,17 +61,17 @@ Message message public static DocumentQuery GetQuery ( - IMessageDataQuery messageDataQuery + IMessageDataDataQuery messageDataDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = messageDataQuery.GetFilter(DataFilterBuilder), - Sort = messageDataQuery.GetSort(SortBuilder), - Skip = messageDataQuery.Skip, - Limit = messageDataQuery.Take, - Options = messageDataQuery.Options as MongoDbQueryOptions, + Filter = messageDataDataQuery.GetFilter(DataFilterBuilder), + Sort = messageDataDataQuery.GetSort(SortBuilder), + Skip = messageDataDataQuery.Skip, + Limit = messageDataDataQuery.Take, + Options = messageDataDataQuery.Options as MongoDbQueryOptions, }; } @@ -82,7 +82,7 @@ public static async Task GetLastStateVersion CancellationToken cancellationToken ) { - var lastStateVersionQuery = new GetLastStateVersionQuery(stateId); + var lastStateVersionQuery = new GetLastStateVersionDataQuery(stateId); var document = await GetQuery(lastStateVersionQuery) .Execute(mongoSession, StateVersionProjection, cancellationToken) diff --git a/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs index 93687d22..d4559219 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs @@ -17,7 +17,7 @@ internal sealed record LeaseDataDocument : MessageDataDocumentBase private static readonly LeaseDataFilterBuilder DataFilterBuilder = new(); - private static readonly LeaseSortBuilder SortBuilder = new(); + private static readonly LeaseDataSortBuilder DataSortBuilder = new(); public required string Scope { get; init; } public required string Label { get; init; } @@ -56,17 +56,17 @@ Message message public static DocumentQuery GetQuery ( - ILeaseQuery leaseQuery + ILeaseDataDataQuery leaseDataDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = leaseQuery.GetFilter(DataFilterBuilder), - Sort = leaseQuery.GetSort(SortBuilder), - Skip = leaseQuery.Skip, - Limit = leaseQuery.Take, - Options = leaseQuery.Options as MongoDbQueryOptions, + Filter = leaseDataDataQuery.GetFilter(DataFilterBuilder), + Sort = leaseDataDataQuery.GetSort(DataSortBuilder), + Skip = leaseDataDataQuery.Skip, + Limit = leaseDataDataQuery.Take, + Options = leaseDataDataQuery.Options as MongoDbQueryOptions, }; } @@ -75,7 +75,7 @@ public static DeleteDocumentsCommand GetDeleteCommand Message message ) { - var deleteLeasesQuery = new DeleteLeasesQuery(message.DeleteLeases); + var deleteLeasesQuery = new DeleteLeasesDataQuery(message.DeleteLeases); return new DeleteDocumentsCommand { diff --git a/src/EntityDb.MongoDb/Documents/TagDataDocument.cs b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs index d04a0004..4347bd81 100644 --- a/src/EntityDb.MongoDb/Documents/TagDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs @@ -17,7 +17,7 @@ internal sealed record TagDataDocument : MessageDataDocumentBase private static readonly TagDataFilterBuilder DataFilterBuilder = new(); - private static readonly TagSortBuilder SortBuilder = new(); + private static readonly TagDataSortBuilder DataSortBuilder = new(); public required string Label { get; init; } public required string Value { get; init; } @@ -54,17 +54,17 @@ Message message public static DocumentQuery GetQuery ( - ITagQuery tagQuery + ITagDataDataQuery tagDataDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = tagQuery.GetFilter(DataFilterBuilder), - Sort = tagQuery.GetSort(SortBuilder), - Skip = tagQuery.Skip, - Limit = tagQuery.Take, - Options = tagQuery.Options as MongoDbQueryOptions, + Filter = tagDataDataQuery.GetFilter(DataFilterBuilder), + Sort = tagDataDataQuery.GetSort(DataSortBuilder), + Skip = tagDataDataQuery.Skip, + Limit = tagDataDataQuery.Take, + Options = tagDataDataQuery.Options as MongoDbQueryOptions, }; } @@ -73,7 +73,7 @@ public static DeleteDocumentsCommand GetDeleteCommand Message message ) { - var deleteTagsQuery = new DeleteTagsQuery(message.StatePointer.Id, message.DeleteTags); + var deleteTagsQuery = new DeleteTagsDataQuery(message.StatePointer.Id, message.DeleteTags); return new DeleteDocumentsCommand { diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs index bb092bd4..24e21f7a 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -29,117 +29,117 @@ IEnvelopeService envelopeService _envelopeService = envelopeService; } - public IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataQuery) + .GetQuery(sourceDataDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateSourceIds(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataQuery) + .GetQuery(messageDataDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateSourceIds(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument - .GetQuery(leaseQuery) + .GetQuery(leaseDataDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateSourceIds(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument - .GetQuery(tagQuery) + .GetQuery(tagDataDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataQuery) + .GetQuery(sourceDataDataQuery) .EnumerateSourceDataStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataQuery) + .GetQuery(messageDataDataQuery) .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument - .GetQuery(leaseQuery) + .GetQuery(leaseDataDataQuery) .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument - .GetQuery(tagQuery) + .GetQuery(tagDataDataQuery) .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataQuery) + .GetQuery(sourceDataDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateDeltas(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataQuery) + .GetQuery(messageDataDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateLeases(ILeaseQuery leaseQuery, + public IAsyncEnumerable EnumerateLeases(ILeaseDataDataQuery leaseDataDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument - .GetQuery(leaseQuery) + .GetQuery(leaseDataDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateTags(ITagQuery tagQuery, + public IAsyncEnumerable EnumerateTags(ITagDataDataQuery tagDataDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument - .GetQuery(tagQuery) + .GetQuery(tagDataDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - ISourceDataQuery sourceDataQuery, + ISourceDataDataQuery sourceDataDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataQuery) + .GetQuery(sourceDataDataQuery) .EnumerateEntitiesAnnotation(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataDataQuery messageDataDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataQuery) + .GetQuery(messageDataDataQuery) .EnumerateAnnotatedSourceData(_mongoSession, _envelopeService, cancellationToken); } diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs similarity index 81% rename from src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs rename to src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs index 0dc4d24a..556f5f3e 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal sealed class LeaseSortBuilder : MessageSortBuilder, ILeaseSortBuilder> +internal sealed class LeaseDataSortBuilder : MessageSortBuilder, ILeaseDataSortBuilder> { public SortDefinition LeaseScope(bool ascending) { diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs similarity index 78% rename from src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs rename to src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs index 6e0622bd..02c5bf65 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal sealed class TagSortBuilder : MessageSortBuilder, ITagSortBuilder> +internal sealed class TagDataSortBuilder : MessageSortBuilder, ITagDataSortBuilder> { public SortDefinition TagLabel(bool ascending) { diff --git a/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs index 4a73c490..d3c79d72 100644 --- a/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs @@ -15,7 +15,7 @@ public sealed class MongoDbSourceSessionOptions public string ConnectionString { get; set; } = default!; /// - /// The name of the database that contains the collections (AgentSignatures, Deltas, Tags, Leases, Aliases) + /// The name of the database that contains the collections (AgentSignatures, Deltas, Leases, Tags) /// public string DatabaseName { get; set; } = default!; diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 2b92ca32..cf6cdcab 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -101,7 +101,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn sourceRepositoryMock .Setup(repository => - repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Returns(() => AsyncEnumerablePolyfill.FromResult(deltas)) .Verifiable(); @@ -143,7 +143,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn sourceRepositoryMock .Verify( - repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), + repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), Times.Once); } diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 6f20874b..8bb3cc7e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -62,7 +62,7 @@ public async IAsyncEnumerable EnumerateSources [EnumeratorCancellation] CancellationToken cancellationToken ) { - var query = new GetDeltasQuery(projectionPointer, default); + var query = new GetDeltasDataQuery(projectionPointer, default); var sourceRepository = await serviceProvider .GetRequiredService() diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataDataDataDataQuery.cs similarity index 84% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataDataDataDataQuery.cs index f7d80f8f..20258f8f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataDataDataDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record CountQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseQuery, ITagQuery +public record CountDataDataDataDataQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseDataDataQuery, ITagDataDataQuery { public int? Skip => null; @@ -26,7 +26,7 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) ); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(ILeaseDataSortBuilder builder) { return builder.Combine ( @@ -49,7 +49,7 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) ); } - public TSort GetSort(ITagSortBuilder builder) + public TSort GetSort(ITagDataSortBuilder builder) { return builder.Combine ( diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataDataDataDataQuery.cs similarity index 84% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataDataDataDataQuery.cs index 5980fb49..adeb055b 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataDataDataDataQuery.cs @@ -5,15 +5,15 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceIdQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseQuery, - ITagQuery +public record SourceIdDataDataDataDataQuery(Id SourceId, object? Options = null) : ISourceDataDataQuery, IMessageDataDataQuery, ILeaseDataDataQuery, + ITagDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.SourceIdIn(SourceId); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(ILeaseDataSortBuilder builder) { return builder.Combine ( @@ -60,7 +60,7 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) return builder.SourceIdIn(SourceId); } - public TSort GetSort(ITagSortBuilder builder) + public TSort GetSort(ITagDataSortBuilder builder) { return builder.Combine ( diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataDataDataDataQuery.cs similarity index 85% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataDataDataDataQuery.cs index 1116d3f8..1ae517b8 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataDataDataDataQuery.cs @@ -5,9 +5,9 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceTimeStampQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataQuery, - IMessageDataQuery, - ILeaseQuery, ITagQuery +public record SourceTimeStampDataDataDataDataQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataDataQuery, + IMessageDataDataQuery, + ILeaseDataDataQuery, ITagDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -18,7 +18,7 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) ); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(ILeaseDataSortBuilder builder) { return builder.Combine ( @@ -77,7 +77,7 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) ); } - public TSort GetSort(ITagSortBuilder builder) + public TSort GetSort(ITagDataSortBuilder builder) { return builder.Combine ( diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataDataDataDataQuery.cs similarity index 83% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataDataDataDataQuery.cs index d2c58e19..dbd580bd 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataDataDataDataQuery.cs @@ -5,15 +5,15 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record StateQuery(Id StateId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, - ILeaseQuery, ITagQuery +public record StateDataDataDataDataQuery(Id StateId, object? Options = null) : ISourceDataDataQuery, IMessageDataDataQuery, + ILeaseDataDataQuery, ITagDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.StateIdIn(StateId); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(ILeaseDataSortBuilder builder) { return builder.Combine ( @@ -55,7 +55,7 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) return builder.StateIdIn(StateId); } - public TSort GetSort(ITagSortBuilder builder) + public TSort GetSort(ITagDataSortBuilder builder) { return builder.Combine ( diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataDataDataDataQuery.cs similarity index 80% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataDataDataDataQuery.cs index fc3d058c..a5e2f648 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataDataDataDataQuery.cs @@ -5,8 +5,8 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record StateVersionQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataQuery, - ILeaseQuery, ITagQuery +public record StateVersionDataDataDataDataQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataDataQuery, + ILeaseDataDataQuery, ITagDataDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -17,7 +17,7 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) ); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(ILeaseDataSortBuilder builder) { return builder.StateVersion(true); } @@ -49,7 +49,7 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) ); } - public TSort GetSort(ITagSortBuilder builder) + public TSort GetSort(ITagDataSortBuilder builder) { return builder.StateVersion(true); } diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 16653b7b..3c30763e 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -108,7 +108,7 @@ bool secondaryPreferred private static async Task TestGetSourceIds ( IServiceScope serviceScope, - ISourceDataQuery query, + ISourceDataDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -128,14 +128,14 @@ Id[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateSourceIds(dataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetSourceIds ( IServiceScope serviceScope, - IMessageDataQuery query, + IMessageDataDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -147,7 +147,7 @@ ExpectedObjects expectedObjects IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateSourceIds(dataQuery.Modify(modifiedQueryOptions)); } Id[] GetExpectedResults(bool invert) @@ -162,7 +162,7 @@ Id[] GetExpectedResults(bool invert) private static async Task TestGetSourceIds ( IServiceScope serviceScope, - ILeaseQuery query, + ILeaseDataDataQuery dataDataQuery, ExpectedObjects expectedObjects ) { @@ -182,14 +182,14 @@ Id[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateSourceIds(dataDataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetSourceIds ( IServiceScope serviceScope, - ITagQuery query, + ITagDataDataQuery dataDataQuery, ExpectedObjects expectedObjects ) { @@ -209,14 +209,14 @@ Id[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateSourceIds(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateSourceIds(dataDataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetStateIds ( IServiceScope serviceScope, - ISourceDataQuery query, + ISourceDataDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -237,7 +237,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -245,7 +245,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetStateIds ( IServiceScope serviceScope, - IMessageDataQuery query, + IMessageDataDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -266,7 +266,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -274,7 +274,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetStateIds ( IServiceScope serviceScope, - ILeaseQuery query, + ILeaseDataDataQuery dataDataQuery, ExpectedObjects expectedObjects ) { @@ -295,7 +295,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(dataDataQuery.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -303,7 +303,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetStateIds ( IServiceScope serviceScope, - ITagQuery query, + ITagDataDataQuery dataDataQuery, ExpectedObjects expectedObjects ) { @@ -324,7 +324,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateStatePointers(query.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(dataDataQuery.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -332,7 +332,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetAgentSignatures ( IServiceScope serviceScope, - ISourceDataQuery query, + ISourceDataDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -352,14 +352,14 @@ object[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateAgentSignatures(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateAgentSignatures(dataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetDeltas ( IServiceScope serviceScope, - IMessageDataQuery query, + IMessageDataDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -379,14 +379,14 @@ object[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateDeltas(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateDeltas(dataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetLeases ( IServiceScope serviceScope, - ILeaseQuery query, + ILeaseDataDataQuery dataDataQuery, ExpectedObjects expectedObjects ) { @@ -406,14 +406,14 @@ ILease[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateLeases(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateLeases(dataDataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetTags ( IServiceScope serviceScope, - ITagQuery query, + ITagDataDataQuery dataDataQuery, ExpectedObjects expectedObjects ) { @@ -425,7 +425,7 @@ ExpectedObjects expectedObjects IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateTags(query.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateTags(dataDataQuery.Modify(modifiedQueryOptions)); } ITag[] GetExpectedResults(bool invert) @@ -532,13 +532,13 @@ private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenRe .GetRequiredService>() .Create("Count"); - var query = new CountQuery(gte, lte, options); + var query = new CountDataDataDataDataQuery(gte, lte, options); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); await TestGetTags(serviceScope, query, expectedObjects); } @@ -815,7 +815,7 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenRet var committed = await writeRepository.Commit(source); - var query = new StateQuery(expectedStateId); + var query = new StateDataDataDataDataQuery(expectedStateId); // ARRANGE ASSERTIONS @@ -868,7 +868,7 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnot var committed = await writeRepository.Commit(source); - var query = new GetDeltasQuery(expectedStateId, default); + var query = new GetDeltasDataQuery(expectedStateId, default); // ARRANGE ASSERTIONS @@ -948,7 +948,7 @@ public async Task GivenMessageWithTagsCommitted_WhenRemovingAllTags_ThenSourceHa }, }; - var tagQuery = new DeleteTagsQuery(stateId, tags); + var deleteTagsQuery = new DeleteTagsDataQuery(stateId, tags); // ARRANGE ASSERTIONS @@ -957,13 +957,13 @@ public async Task GivenMessageWithTagsCommitted_WhenRemovingAllTags_ThenSourceHa // ACT var initialTags = await writeRepository - .EnumerateTags(tagQuery) + .EnumerateTags(deleteTagsQuery) .ToArrayAsync(); var finalSourceCommitted = await writeRepository.Commit(finalSource); var finalTags = await writeRepository - .EnumerateTags(tagQuery) + .EnumerateTags(deleteTagsQuery) .ToArrayAsync(); // ASSERT @@ -1021,7 +1021,7 @@ public async Task GivenMessageWithLeasesCommitted_WhenRemovingAllLeases_ThenSour }, }; - var leaseQuery = new DeleteLeasesQuery(leases); + var leaseQuery = new DeleteLeasesDataQuery(leases); // ARRANGE ASSERTIONS @@ -1064,7 +1064,7 @@ public async Task GivenMessageCommitted_WhenQueryingForVersionOne_ThenReturnTheE var source = CreateSource(new[] { 1ul }); - var versionOneQuery = new StateVersionQuery(new Version(1), new Version(1)); + var versionOneQuery = new StateVersionDataDataDataDataQuery(new Version(1), new Version(1)); // ACT @@ -1099,7 +1099,7 @@ public async Task GivenTwoMessagesCommitted_WhenQueryingForVersionTwo_ThenReturn var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); var secondSource = CreateSource(new[] { 2ul }, stateId: stateId); - var versionTwoQuery = new StateVersionQuery(new Version(2), new Version(2)); + var versionTwoQuery = new StateVersionDataDataDataDataQuery(new Version(2), new Version(2)); // ACT @@ -1191,17 +1191,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then gte.ShouldNotBeNull(); lte.ShouldNotBeNull(); - var query = new SourceTimeStampQuery(gte.Value, lte.Value); + var query = new SourceTimeStampDataDataDataDataQuery(gte.Value, lte.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1271,17 +1271,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnE sourceId.ShouldNotBeNull(); - var query = new SourceIdQuery(sourceId.Value); + var query = new SourceIdDataDataDataDataQuery(sourceId.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1352,17 +1352,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateId_ThenReturnEx stateId.ShouldNotBeNull(); - var query = new StateQuery(stateId.Value); + var query = new StateDataDataDataDataQuery(stateId.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1419,7 +1419,7 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateVersion_ThenRet var sources = new List { source }; - var query = new StateVersionQuery(new Version(gte), new Version(lte)); + var query = new StateVersionDataDataDataDataQuery(new Version(gte), new Version(lte)); await PutSources(serviceScope, sources); await TestGetDeltas(serviceScope, query, expectedObjects); diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs index 59328f04..cb801331 100644 --- a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -27,69 +27,69 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti sourceRepositoryMock .Setup(repository => - repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) + repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateLeases(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateLeases(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateTags(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateTags(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), + repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock @@ -103,21 +103,21 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti // ACT var sourceIdsFromSourceDataQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(ISourceDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateSourceIds(default(ISourceDataDataQuery)!).ToArrayAsync(); var sourceIdsFromMessageDataQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageDataQuery)!).ToArrayAsync(); - var sourceIdsFromLeaseQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseQuery)!).ToArrayAsync(); - var sourceIdsFromTagQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(ITagQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageDataDataQuery)!).ToArrayAsync(); + var sourceIdsFromLeaseDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseDataDataQuery)!).ToArrayAsync(); + var sourceIdsFromTagDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ITagDataDataQuery)!).ToArrayAsync(); var statePointersFromSourceDataQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(ISourceDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateStatePointers(default(ISourceDataDataQuery)!).ToArrayAsync(); var statePointersFromMessageDataQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(IMessageDataQuery)!).ToArrayAsync(); - var statePointersFromLeaseQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(ILeaseQuery)!).ToArrayAsync(); - var statePointersFromTagQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(ITagQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateStatePointers(default(IMessageDataDataQuery)!).ToArrayAsync(); + var statePointersFromLeaseDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ILeaseDataDataQuery)!).ToArrayAsync(); + var statePointersFromTagDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ITagDataDataQuery)!).ToArrayAsync(); var agentSignatures = await tryCatchSourceRepository.EnumerateAgentSignatures(default!).ToArrayAsync(); var deltas = @@ -135,12 +135,12 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti sourceIdsFromSourceDataQuery.ShouldBeEmpty(); sourceIdsFromMessageDataQuery.ShouldBeEmpty(); - sourceIdsFromLeaseQuery.ShouldBeEmpty(); - sourceIdsFromTagQuery.ShouldBeEmpty(); + sourceIdsFromLeaseDataQuery.ShouldBeEmpty(); + sourceIdsFromTagDataQuery.ShouldBeEmpty(); statePointersFromSourceDataQuery.ShouldBeEmpty(); statePointersFromMessageDataQuery.ShouldBeEmpty(); - statePointersFromLeaseQuery.ShouldBeEmpty(); - statePointersFromTagQuery.ShouldBeEmpty(); + statePointersFromLeaseDataQuery.ShouldBeEmpty(); + statePointersFromTagDataQuery.ShouldBeEmpty(); agentSignatures.ShouldBeEmpty(); deltas.ShouldBeEmpty(); leases.ShouldBeEmpty(); diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index 65f84404..c66ed23d 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -36,14 +36,14 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerable.Empty()); // Second query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerable.Empty()); await using var serviceScope = CreateServiceScope(serviceCollection => @@ -119,7 +119,7 @@ public async Task GivenNewStream_WhenStagingNewMessageKey_ThenCommitReturnsTrue( var committed = await writeRepository.Commit(); var statePointerCount = await writeRepository.SourceRepository - .EnumerateStatePointers(new MatchingLeasesQuery(streamKeyLease, messageKeyLease)) + .EnumerateStatePointers(new MatchingLeasesDataQuery(streamKeyLease, messageKeyLease)) .CountAsync(); // ASSERT @@ -145,14 +145,14 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); // First query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerable.Empty()); await using var serviceScope = CreateServiceScope(serviceCollection => @@ -240,7 +240,7 @@ public async Task GivenExistingStream_WhenStagingNewMessageKey_ThenCommitReturns var secondCommitted = await writeRepository.Commit(); var statePointerCount = await writeRepository.SourceRepository - .EnumerateStatePointers(new MatchingLeasesQuery(streamKeyLease, messageKeyLease1, messageKeyLease2)) + .EnumerateStatePointers(new MatchingLeasesDataQuery(streamKeyLease, messageKeyLease1, messageKeyLease2)) .CountAsync(); // ASSERT @@ -264,14 +264,14 @@ public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenSta sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); // Second query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); await using var serviceScope = CreateServiceScope(serviceCollection => diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 9e90b76a..1fb45a6f 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -526,7 +526,7 @@ protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( sourceRepositoryMock .Setup(repository => - repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(deltas)); return GetMockedSourceRepositoryFactory(sourceRepositoryMock, new List()); From b42f041f2e2f669ac63d22f7750be038c3ae503c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 11 Jan 2024 19:28:18 -0800 Subject: [PATCH 74/93] fix: names got messed up --- .../Projections/IProjection.cs | 2 +- .../Sources/ISourceRepository.cs | 84 +++++++------- ...FilterBuilder.cs => IDataFilterBuilder.cs} | 2 +- .../IMessageDataFilterBuilder.cs | 2 +- .../ISourceDataFilterBuilder.cs | 2 +- ...aseDataDataQuery.cs => ILeaseDataQuery.cs} | 2 +- ...eDataDataQuery.cs => IMessageDataQuery.cs} | 2 +- ...ceDataDataQuery.cs => ISourceDataQuery.cs} | 2 +- ...{ITagDataDataQuery.cs => ITagDataQuery.cs} | 2 +- .../{ISortBuilder.cs => IDataSortBuilder.cs} | 2 +- .../SortBuilders/IMessageDataSortBuilder.cs | 2 +- .../SortBuilders/ISourceDataSortBuilder.cs | 2 +- .../FilterBuilders/FilterBuilderExtensions.cs | 38 +++--- ...DataQuery.cs => ModifiedLeaseDataQuery.cs} | 12 +- ...taQuery.cs => ModifiedMessageDataQuery.cs} | 12 +- .../Queries/Modified/ModifiedQueryOptions.cs | 2 +- ...ataQuery.cs => ModifiedSourceDataQuery.cs} | 12 +- ...taDataQuery.cs => ModifiedTagDataQuery.cs} | 12 +- .../Sources/Queries/QueryExtensions.cs | 44 +++---- .../ReverseLeaseDataSortBuilder.cs | 2 +- .../ReverseMessageDataSortBuilder.cs | 2 +- .../SortBuilders/ReverseSortBuilderBase.cs | 10 +- .../ReverseSourceDataSortBuilder.cs | 2 +- .../SortBuilders/ReverseTagDataSortBuilder.cs | 2 +- .../Queries/Standard/DeleteLeasesDataQuery.cs | 2 +- .../Queries/Standard/DeleteTagsDataQuery.cs | 2 +- .../Queries/Standard/GetDeltasDataQuery.cs | 2 +- .../Standard/GetLastStateVersionDataQuery.cs | 2 +- .../Queries/Standard/GetSourceDataQuery.cs | 2 +- .../Standard/MatchingLeaseDataQuery.cs | 2 +- .../Standard/MatchingLeasesDataQuery.cs | 2 +- .../Sources/SourceRepositoryExtensions.cs | 8 +- .../Sources/SourceRepositoryWrapper.cs | 56 ++++----- .../Documents/AgentSignatureDocument.cs | 14 +-- .../Documents/DeltaDataDocument.cs | 14 +-- .../Documents/LeaseDataDocument.cs | 12 +- .../Documents/SourceDataDocumentBase.cs | 2 +- .../Documents/TagDataDocument.cs | 12 +- .../Sources/MongoDbSourceRepository.cs | 56 ++++----- ...uilderBase.cs => DataFilterBuilderBase.cs} | 2 +- .../MessageDataFilterBuilder.cs | 2 +- .../FilterBuilders/SourceDataFilterBuilder.cs | 2 +- ...tBuilderBase.cs => DataSortBuilderBase.cs} | 2 +- .../SortBuilders/LeaseDataSortBuilder.cs | 2 +- ...rtBuilder.cs => MessageDataSortBuilder.cs} | 2 +- .../SortBuilders/SourceDataSortBuilder.cs | 2 +- .../SortBuilders/TagDataSortBuilder.cs | 2 +- .../Entities/EntityTests.cs | 4 +- .../FilterBuilderExtensionsTests.cs | 4 +- ...DataDataDataQuery.cs => CountDataQuery.cs} | 2 +- ...aDataDataQuery.cs => SourceIdDataQuery.cs} | 4 +- ...taQuery.cs => SourceTimeStampDataQuery.cs} | 6 +- ...DataDataDataQuery.cs => StateDataQuery.cs} | 4 +- ...aDataQuery.cs => StateVersionDataQuery.cs} | 4 +- .../Sources/SourceTests.cs | 108 +++++++++--------- .../Sources/TryCatchSourceRepositoryTests.cs | 44 +++---- .../Streams/StreamTests.cs | 12 +- test/EntityDb.Common.Tests/TestsBase.cs | 2 +- 58 files changed, 327 insertions(+), 327 deletions(-) rename src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/{IFilterBuilder.cs => IDataFilterBuilder.cs} (96%) rename src/EntityDb.Abstractions/Sources/Queries/{ILeaseDataDataQuery.cs => ILeaseDataQuery.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/{IMessageDataDataQuery.cs => IMessageDataQuery.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/{ISourceDataDataQuery.cs => ISourceDataQuery.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/{ITagDataDataQuery.cs => ITagDataQuery.cs} (93%) rename src/EntityDb.Abstractions/Sources/Queries/SortBuilders/{ISortBuilder.cs => IDataSortBuilder.cs} (95%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedLeaseDataDataQuery.cs => ModifiedLeaseDataQuery.cs} (58%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedMessageDataDataQuery.cs => ModifiedMessageDataQuery.cs} (58%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedSourceDataDataQuery.cs => ModifiedSourceDataQuery.cs} (58%) rename src/EntityDb.Common/Sources/Queries/Modified/{ModifiedTagDataDataQuery.cs => ModifiedTagDataQuery.cs} (59%) rename src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/{FilterBuilderBase.cs => DataFilterBuilderBase.cs} (93%) rename src/EntityDb.MongoDb/Sources/Queries/SortBuilders/{SortBuilderBase.cs => DataSortBuilderBase.cs} (89%) rename src/EntityDb.MongoDb/Sources/Queries/SortBuilders/{MessageSortBuilder.cs => MessageDataSortBuilder.cs} (87%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{CountDataDataDataDataQuery.cs => CountDataQuery.cs} (92%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{SourceIdDataDataDataDataQuery.cs => SourceIdDataQuery.cs} (91%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{SourceTimeStampDataDataDataDataQuery.cs => SourceTimeStampDataQuery.cs} (91%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{StateDataDataDataDataQuery.cs => StateDataQuery.cs} (90%) rename test/EntityDb.Common.Tests/Implementations/Sources/Queries/{StateVersionDataDataDataDataQuery.cs => StateVersionDataQuery.cs} (89%) diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index 1ddbf4fa..c3f97086 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -18,7 +18,7 @@ public interface IProjection : IState void Mutate(Source source); /// - /// Returns a that finds sources that need to be passed to the reducer. + /// Returns a that finds sources that need to be passed to the reducer. /// /// A service provider for fetching repositories. /// A pointer to the desired projection state diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs index 6f0ea530..b63c1555 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -14,128 +14,128 @@ public interface ISourceRepository : IDisposableResource /// /// Returns the source ids which are found by a message group query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(ISourceDataDataQuery sourceDataDataQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(IMessageDataDataQuery messageDataDataQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a lease query. /// - /// The lease query. + /// The lease query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(ILeaseDataDataQuery leaseDataDataQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default); /// /// Returns the source ids which are found by a tag query. /// - /// The tag query. + /// The tag query. /// A cancellation token. - /// The source ids which are found by . - IAsyncEnumerable EnumerateSourceIds(ITagDataDataQuery tagDataDataQuery, + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a agentSignature query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ISourceDataDataQuery sourceDataDataQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(IMessageDataDataQuery messageDataDataQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a lease query. /// - /// The lease query. + /// The lease query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ILeaseDataDataQuery leaseDataDataQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default); /// /// Returns the state pointers which are found by a tag query. /// - /// The tag query. + /// The tag query. /// A cancellation token. - /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ITagDataDataQuery tagDataDataQuery, + /// The state pointers which are found by . + IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default); /// /// Returns the agentSignatures which are found by an message group query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The agent signatures which are found by . - IAsyncEnumerable EnumerateAgentSignatures(ISourceDataDataQuery sourceDataDataQuery, + /// The agent signatures which are found by . + IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the deltas which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The deltas which are found by . - IAsyncEnumerable EnumerateDeltas(IMessageDataDataQuery messageDataDataQuery, + /// The deltas which are found by . + IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// /// Returns the leases which are found by a lease query. /// - /// The lease query. + /// The lease query. /// A cancellation token. - /// The leases which are found by . - IAsyncEnumerable EnumerateLeases(ILeaseDataDataQuery leaseDataDataQuery, + /// The leases which are found by . + IAsyncEnumerable EnumerateLeases(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default); /// /// Returns the tags which are found by a tag query. /// - /// The tag query. + /// The tag query. /// A cancellation token. - /// The tags which are found by . - IAsyncEnumerable EnumerateTags(ITagDataDataQuery tagDataDataQuery, + /// The tags which are found by . + IAsyncEnumerable EnumerateTags(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default); /// /// Returns the annotated agent signatures which are found by a message group query. /// - /// The source data query. + /// The source data query. /// A cancellation token. - /// The annotated agent signatures which are found by . + /// The annotated agent signatures which are found by . IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - ISourceDataDataQuery sourceDataDataQuery, + ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// /// Returns the annotated deltas which are found by a message query. /// - /// The message data query. + /// The message data query. /// A cancellation token. - /// The annotated deltas which are found by . - IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataDataQuery messageDataDataQuery, + /// The annotated deltas which are found by . + IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs similarity index 96% rename from src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs index 01297af5..8150fe70 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs @@ -7,7 +7,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// agent signatures, deltas, facts, and tags. /// /// The type of filter used by the repository. -public interface IFilterBuilder +public interface IDataFilterBuilder { /// /// Returns a that only includes objects with a source diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs index c1a144f4..03cd48d0 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs @@ -9,7 +9,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// deltas, facts, and tags. /// /// The type of filter used by the repository. -public interface IMessageDataFilterBuilder : IFilterBuilder +public interface IMessageDataFilterBuilder : IDataFilterBuilder { /// /// Returns a that only includes objects with a state diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs index 25a7010a..19293691 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs @@ -8,7 +8,7 @@ namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// agent signatures /// /// The type of filter used by the repository. -public interface ISourceDataFilterBuilder : IFilterBuilder +public interface ISourceDataFilterBuilder : IDataFilterBuilder { /// /// Returns a that only includes objects with any state diff --git a/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataDataQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataQuery.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/ILeaseDataDataQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ILeaseDataQuery.cs index 7ae12f03..f4662378 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataDataQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on leases. /// -public interface ILeaseDataDataQuery : IDataQuery +public interface ILeaseDataQuery : IDataQuery { /// /// Returns a built from a lease filter builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/IMessageDataDataQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/IMessageDataDataQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs index f5879451..26be82a4 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/IMessageDataDataQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on messages. /// -public interface IMessageDataDataQuery : IDataQuery +public interface IMessageDataQuery : IDataQuery { /// /// Returns a built from a message filter builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/ISourceDataDataQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/ISourceDataDataQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs index f29bbd78..f9b844bc 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ISourceDataDataQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on agentSignatures. /// -public interface ISourceDataDataQuery : IDataQuery +public interface ISourceDataQuery : IDataQuery { /// /// Returns a built from a agentSignature filter builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/ITagDataDataQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ITagDataQuery.cs similarity index 93% rename from src/EntityDb.Abstractions/Sources/Queries/ITagDataDataQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ITagDataQuery.cs index 40bc64a7..4329c85c 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/ITagDataDataQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ITagDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Abstractions.Sources.Queries; /// /// Abstracts a query on tags. /// -public interface ITagDataDataQuery : IDataQuery +public interface ITagDataQuery : IDataQuery { /// /// Returns a built from a tag filter builder. diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IDataSortBuilder.cs similarity index 95% rename from src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IDataSortBuilder.cs index 6feae84c..1c084f49 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IDataSortBuilder.cs @@ -5,7 +5,7 @@ /// agentSignatures, deltas, facts, and leases. /// /// The type of sort used by the repository. -public interface ISortBuilder +public interface IDataSortBuilder { /// /// Returns a that orders objects by source timestamp. diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs index 8339dda1..ebee5d7a 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs @@ -4,7 +4,7 @@ /// Builds a for a message query. /// /// The type of sort used by the repository. -public interface IMessageDataSortBuilder : ISortBuilder +public interface IMessageDataSortBuilder : IDataSortBuilder { /// /// Returns a that orders objects by state id. diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs index 96cf9dda..87f45db9 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs @@ -4,7 +4,7 @@ /// Builds a for a agentSignature query. /// /// The type of sort used by the repository. -public interface ISourceDataSortBuilder : ISortBuilder +public interface ISourceDataSortBuilder : IDataSortBuilder { /// /// Returns a that orders objects by state ids. diff --git a/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs b/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs index f8193fba..c32167f9 100644 --- a/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs @@ -11,18 +11,18 @@ public static class FilterBuilderExtensions /// Returns a that excludes objects which do match all filters in a set of filters. /// /// The type of filter used by the repository. - /// The filter builder. + /// The filter builder. /// The set of filters. /// /// A that excludes objects which do match all filters in /// . /// - public static TFilter Nand(this IFilterBuilder filterBuilder, + public static TFilter Nand(this IDataFilterBuilder dataFilterBuilder, params TFilter[] filters) { - return filterBuilder.Not + return dataFilterBuilder.Not ( - filterBuilder.And(filters) + dataFilterBuilder.And(filters) ); } @@ -30,17 +30,17 @@ public static TFilter Nand(this IFilterBuilder filterBuilder, /// Returns a that excludes objects which do match any filter in a set of filters. /// /// The type of filter used by the repository. - /// The filter builder. + /// The filter builder. /// The set of filters. /// /// A that excludes objects which do match any filter in /// . /// - public static TFilter Nor(this IFilterBuilder filterBuilder, params TFilter[] filters) + public static TFilter Nor(this IDataFilterBuilder dataFilterBuilder, params TFilter[] filters) { - return filterBuilder.Not + return dataFilterBuilder.Not ( - filterBuilder.Or(filters) + dataFilterBuilder.Or(filters) ); } @@ -48,26 +48,26 @@ public static TFilter Nor(this IFilterBuilder filterBuilder, p /// Returns a that only includes objects which only match one filter. /// /// The type of filter used by the repository. - /// The filter builder. + /// The filter builder. /// The first filter. /// The second filter. /// /// A that only includes objects which only match or /// only match . /// - public static TFilter Xor(this IFilterBuilder filterBuilder, TFilter filterA, + public static TFilter Xor(this IDataFilterBuilder dataFilterBuilder, TFilter filterA, TFilter filterB) { - return filterBuilder.Or + return dataFilterBuilder.Or ( - filterBuilder.And + dataFilterBuilder.And ( filterA, - filterBuilder.Not(filterB) + dataFilterBuilder.Not(filterB) ), - filterBuilder.And + dataFilterBuilder.And ( - filterBuilder.Not(filterA), + dataFilterBuilder.Not(filterA), filterB ) ); @@ -77,19 +77,19 @@ public static TFilter Xor(this IFilterBuilder filterBuilder, T /// Returns a that excludes objects which only match one filter. /// /// The type of filter used by the repository. - /// The filter builder. + /// The filter builder. /// The first filter. /// The second filter. /// /// A that excludes objects which only match or only /// match . /// - public static TFilter Xnor(this IFilterBuilder filterBuilder, TFilter filterA, + public static TFilter Xnor(this IDataFilterBuilder dataFilterBuilder, TFilter filterA, TFilter filterB) { - return filterBuilder.Not + return dataFilterBuilder.Not ( - filterBuilder.Xor(filterA, filterB) + dataFilterBuilder.Xor(filterA, filterB) ); } } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataQuery.cs similarity index 58% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataDataQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataQuery.cs index d6812239..8be2b6a9 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedLeaseDataDataQuery : ModifiedQueryBase, ILeaseDataDataQuery +internal sealed record ModifiedLeaseDataQuery : ModifiedQueryBase, ILeaseDataQuery { - public required ILeaseDataDataQuery LeaseDataDataQuery { get; init; } - protected override IDataQuery DataQuery => LeaseDataDataQuery; + public required ILeaseDataQuery LeaseDataQuery { get; init; } + protected override IDataQuery DataQuery => LeaseDataQuery; public TFilter GetFilter(ILeaseDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.Not ( - LeaseDataDataQuery.GetFilter(builder) + LeaseDataQuery.GetFilter(builder) ); } - return LeaseDataDataQuery.GetFilter(builder); + return LeaseDataQuery.GetFilter(builder); } public TSort? GetSort(ILeaseDataSortBuilder builder) { - return LeaseDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + return LeaseDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs similarity index 58% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataDataQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs index ca7b0831..6e58577b 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedMessageDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedMessageDataDataQuery : ModifiedQueryBase, IMessageDataDataQuery +internal sealed record ModifiedMessageDataQuery : ModifiedQueryBase, IMessageDataQuery { - public required IMessageDataDataQuery MessageDataDataQuery { get; init; } - protected override IDataQuery DataQuery => MessageDataDataQuery; + public required IMessageDataQuery MessageDataQuery { get; init; } + protected override IDataQuery DataQuery => MessageDataQuery; public TFilter GetFilter(IMessageDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(IMessageDataFilterBuilder builder) { return builder.Not ( - MessageDataDataQuery.GetFilter(builder) + MessageDataQuery.GetFilter(builder) ); } - return MessageDataDataQuery.GetFilter(builder); + return MessageDataQuery.GetFilter(builder); } public TSort? GetSort(IMessageDataSortBuilder builder) { - return MessageDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + return MessageDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs index 701382be..422b7c61 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs @@ -10,7 +10,7 @@ public record ModifiedQueryOptions { /// /// If true, then the new query will return the value of - /// + /// /// applied to the filter of the original query. Otherwise, the new query will return the same filter as the original /// query. /// diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs similarity index 58% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataDataQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs index 51e8d362..7d2e239f 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedSourceDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedSourceDataDataQuery : ModifiedQueryBase, ISourceDataDataQuery +internal sealed record ModifiedSourceDataQuery : ModifiedQueryBase, ISourceDataQuery { - public required ISourceDataDataQuery SourceDataDataQuery { get; init; } - protected override IDataQuery DataQuery => SourceDataDataQuery; + public required ISourceDataQuery SourceDataQuery { get; init; } + protected override IDataQuery DataQuery => SourceDataQuery; public TFilter GetFilter(ISourceDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(ISourceDataFilterBuilder builder) { return builder.Not ( - SourceDataDataQuery.GetFilter(builder) + SourceDataQuery.GetFilter(builder) ); } - return SourceDataDataQuery.GetFilter(builder); + return SourceDataQuery.GetFilter(builder); } public TSort? GetSort(ISourceDataSortBuilder builder) { - return SourceDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + return SourceDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataQuery.cs similarity index 59% rename from src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataDataQuery.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataQuery.cs index e5c45e9e..e6df3874 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataQuery.cs @@ -5,10 +5,10 @@ namespace EntityDb.Common.Sources.Queries.Modified; -internal sealed record ModifiedTagDataDataQuery : ModifiedQueryBase, ITagDataDataQuery +internal sealed record ModifiedTagDataQuery : ModifiedQueryBase, ITagDataQuery { - public required ITagDataDataQuery TagDataDataQuery { get; init; } - protected override IDataQuery DataQuery => TagDataDataQuery; + public required ITagDataQuery TagDataQuery { get; init; } + protected override IDataQuery DataQuery => TagDataQuery; public TFilter GetFilter(ITagDataFilterBuilder builder) { @@ -16,16 +16,16 @@ public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.Not ( - TagDataDataQuery.GetFilter(builder) + TagDataQuery.GetFilter(builder) ); } - return TagDataDataQuery.GetFilter(builder); + return TagDataQuery.GetFilter(builder); } public TSort? GetSort(ITagDataSortBuilder builder) { - return TagDataDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + return TagDataQuery.GetSort(ModifiedQueryOptions.ReverseSort ? builder.Reverse() : builder); } diff --git a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs index 848cb554..69a85e6b 100644 --- a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs @@ -9,59 +9,59 @@ namespace EntityDb.Common.Sources.Queries; public static class QueryExtensions { /// - /// Returns a new, modified . The way in which + /// Returns a new, modified . The way in which /// it is modified depends on the parameters of this extension method. /// - /// The source data query. + /// The source data query. /// The options for modifying the query. - /// A new, modified . - public static ISourceDataDataQuery Modify(this ISourceDataDataQuery sourceDataDataQuery, + /// A new, modified . + public static ISourceDataQuery Modify(this ISourceDataQuery sourceDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedSourceDataDataQuery + return new ModifiedSourceDataQuery { - ModifiedQueryOptions = modifiedQueryOptions, SourceDataDataQuery = sourceDataDataQuery, + ModifiedQueryOptions = modifiedQueryOptions, SourceDataQuery = sourceDataQuery, }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters + /// Returns a new, modified . The way in which it is modified depends on the parameters /// of /// this extension method. /// - /// The message data query. + /// The message data query. /// The options for modifying the query. - /// A new, modified . - public static IMessageDataDataQuery Modify(this IMessageDataDataQuery messageDataDataQuery, + /// A new, modified . + public static IMessageDataQuery Modify(this IMessageDataQuery messageDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedMessageDataDataQuery + return new ModifiedMessageDataQuery { - ModifiedQueryOptions = modifiedQueryOptions, MessageDataDataQuery = messageDataDataQuery, + ModifiedQueryOptions = modifiedQueryOptions, MessageDataQuery = messageDataQuery, }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// Returns a new, modified . The way in which it is modified depends on the parameters of /// this extension method. /// - /// The lease query. + /// The lease query. /// The options for modifying the query. - /// A new, modified . - public static ILeaseDataDataQuery Modify(this ILeaseDataDataQuery leaseDataDataQuery, ModifiedQueryOptions modifiedQueryOptions) + /// A new, modified . + public static ILeaseDataQuery Modify(this ILeaseDataQuery leaseDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedLeaseDataDataQuery { ModifiedQueryOptions = modifiedQueryOptions, LeaseDataDataQuery = leaseDataDataQuery }; + return new ModifiedLeaseDataQuery { ModifiedQueryOptions = modifiedQueryOptions, LeaseDataQuery = leaseDataQuery }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of this + /// Returns a new, modified . The way in which it is modified depends on the parameters of this /// extension method. /// - /// The tag query. + /// The tag query. /// The options for modifying the query. - /// A new, modified . - public static ITagDataDataQuery Modify(this ITagDataDataQuery tagDataDataQuery, ModifiedQueryOptions modifiedQueryOptions) + /// A new, modified . + public static ITagDataQuery Modify(this ITagDataQuery tagDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedTagDataDataQuery { ModifiedQueryOptions = modifiedQueryOptions, TagDataDataQuery = tagDataDataQuery }; + return new ModifiedTagDataQuery { ModifiedQueryOptions = modifiedQueryOptions, TagDataQuery = tagDataQuery }; } } diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs index ddf62810..f42d7c7a 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseLeaseDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.SortBuilders; internal sealed record ReverseLeaseDataSortBuilder : ReverseSortBuilderBase, ILeaseDataSortBuilder { public required ILeaseDataSortBuilder LeaseDataSortBuilder { get; init; } - protected override ISortBuilder SortBuilder => LeaseDataSortBuilder; + protected override IDataSortBuilder DataSortBuilder => LeaseDataSortBuilder; public TSort StateId(bool ascending) { diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs index 063547ba..e6357ea4 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs @@ -6,7 +6,7 @@ internal sealed record ReverseMessageDataSortBuilder : ReverseSortBuilder IMessageDataSortBuilder { public required IMessageDataSortBuilder MessageDataSortBuilder { get; init; } - protected override ISortBuilder SortBuilder => MessageDataSortBuilder; + protected override IDataSortBuilder DataSortBuilder => MessageDataSortBuilder; public TSort StateId(bool ascending) { diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs index 8fb844b0..86e8e172 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs @@ -4,25 +4,25 @@ namespace EntityDb.Common.Sources.Queries.SortBuilders; internal abstract record ReverseSortBuilderBase { - protected abstract ISortBuilder SortBuilder { get; } + protected abstract IDataSortBuilder DataSortBuilder { get; } public TSort SourceTimeStamp(bool ascending) { - return SortBuilder.SourceTimeStamp(!ascending); + return DataSortBuilder.SourceTimeStamp(!ascending); } public TSort SourceId(bool ascending) { - return SortBuilder.SourceId(!ascending); + return DataSortBuilder.SourceId(!ascending); } public TSort DataType(bool ascending) { - return SortBuilder.DataType(!ascending); + return DataSortBuilder.DataType(!ascending); } public TSort Combine(params TSort[] sorts) { - return SortBuilder.Combine(sorts); + return DataSortBuilder.Combine(sorts); } } diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs index 130d98a0..432be1d6 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSourceDataSortBuilder.cs @@ -6,7 +6,7 @@ internal sealed record ReverseSourceDataSortBuilder : ReverseSortBuilderB ISourceDataSortBuilder { public required ISourceDataSortBuilder SourceDataSortBuilder { get; init; } - protected override ISortBuilder SortBuilder => SourceDataSortBuilder; + protected override IDataSortBuilder DataSortBuilder => SourceDataSortBuilder; public TSort StateIds(bool ascending) { diff --git a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs index f5ca19ef..e2d98321 100644 --- a/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseTagDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.SortBuilders; internal sealed record ReverseTagDataSortBuilder : ReverseSortBuilderBase, ITagDataSortBuilder { public required ITagDataSortBuilder TagDataSortBuilder { get; init; } - protected override ISortBuilder SortBuilder => TagDataSortBuilder; + protected override IDataSortBuilder DataSortBuilder => TagDataSortBuilder; public TSort StateId(bool ascending) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs index 71a4af75..e9386d65 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs @@ -6,7 +6,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record DeleteLeasesDataQuery(IReadOnlyCollection Leases, - object? Options = null) : ILeaseDataDataQuery + object? Options = null) : ILeaseDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs index 6f44562f..6d775f7a 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs @@ -7,7 +7,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record DeleteTagsDataQuery - (Id StateId, IReadOnlyCollection Tags, object? Options = null) : ITagDataDataQuery + (Id StateId, IReadOnlyCollection Tags, object? Options = null) : ITagDataQuery { public TFilter GetFilter(ITagDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs index 8585252d..07ea0744 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs @@ -7,7 +7,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; internal sealed record GetDeltasDataQuery(Pointer StatePointer, Version PersistedStateVersion, - object? Options = null) : IMessageDataDataQuery + object? Options = null) : IMessageDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs index 0237acde..1aba17cc 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetLastStateVersionDataQuery(Id StateId, object? Options = null) : IMessageDataDataQuery +internal sealed record GetLastStateVersionDataQuery(Id StateId, object? Options = null) : IMessageDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs index 835cdae1..b51d253c 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetSourceDataQuery(Id SourceId) : ISourceDataDataQuery, IMessageDataDataQuery +internal sealed record GetSourceDataQuery(Id SourceId) : ISourceDataQuery, IMessageDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs index 194a97de..4fc6d07c 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record MatchingLeaseDataQuery(ILease Lease) : ILeaseDataDataQuery +internal sealed record MatchingLeaseDataQuery(ILease Lease) : ILeaseDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs index 611384ea..693f91e9 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record MatchingLeasesDataQuery(params ILease[] Leases) : ILeaseDataDataQuery +internal sealed record MatchingLeasesDataQuery(params ILease[] Leases) : ILeaseDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs index cdd404e4..b6301bb2 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs @@ -22,11 +22,11 @@ public static IAsyncEnumerable EnumerateSourceIds(this ISourceRepository sou { return dataQuery switch { - ISourceDataDataQuery sourceDataQuery => sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken), - IMessageDataDataQuery messageDataQuery => sourceRepository.EnumerateSourceIds(messageDataQuery, + ISourceDataQuery sourceDataQuery => sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken), + IMessageDataQuery messageDataQuery => sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken), - ILeaseDataDataQuery leaseDataQuery => sourceRepository.EnumerateSourceIds(leaseDataQuery, cancellationToken), - ITagDataDataQuery tagDataQuery => sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken), + ILeaseDataQuery leaseDataQuery => sourceRepository.EnumerateSourceIds(leaseDataQuery, cancellationToken), + ITagDataQuery tagDataQuery => sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken), _ => AsyncEnumerable.Empty(), }; } diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs index a8d8b316..348f5f8d 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -16,90 +16,90 @@ protected SourceRepositoryWrapper(ISourceRepository sourceRepository) _sourceRepository = sourceRepository; } - public IAsyncEnumerable EnumerateSourceIds(ISourceDataDataQuery sourceDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(sourceDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(ILeaseDataDataQuery leaseDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(leaseDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(leaseDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateSourceIds(ITagDataDataQuery tagDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ISourceDataDataQuery sourceDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ILeaseDataDataQuery leaseDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ITagDataDataQuery tagDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataDataQuery sourceDataDataQuery, + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(sourceDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateDeltas(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateLeases(ILeaseDataDataQuery leaseDataDataQuery, + public IAsyncEnumerable EnumerateLeases(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateLeases(leaseDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateLeases(leaseDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateTags(ITagDataDataQuery tagDataDataQuery, + public IAsyncEnumerable EnumerateTags(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateTags(tagDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateTags(tagDataQuery, cancellationToken)); } public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - ISourceDataDataQuery sourceDataDataQuery, + ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => - _sourceRepository.EnumerateAnnotatedAgentSignatures(sourceDataDataQuery, cancellationToken)); + _sourceRepository.EnumerateAnnotatedAgentSignatures(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { - return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageDataDataQuery, cancellationToken)); + return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageDataQuery, cancellationToken)); } public virtual Task Commit(Source source, diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 0b84c85b..e646c937 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -13,7 +13,7 @@ internal sealed record AgentSignatureDocument : SourceDataDocumentBase { public const string CollectionName = "AgentSignatures"; - private static readonly SourceDataSortBuilder SortBuilder = new(); + private static readonly SourceDataSortBuilder DataSortBuilder = new(); public static InsertDocumentsCommand GetInsertCommand ( @@ -57,17 +57,17 @@ Source source public static DocumentQuery GetQuery ( - ISourceDataDataQuery sourceDataDataQuery + ISourceDataQuery sourceDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = sourceDataDataQuery.GetFilter(FilterBuilder), - Sort = sourceDataDataQuery.GetSort(SortBuilder), - Skip = sourceDataDataQuery.Skip, - Limit = sourceDataDataQuery.Take, - Options = sourceDataDataQuery.Options as MongoDbQueryOptions, + Filter = sourceDataQuery.GetFilter(DataFilterBuilder), + Sort = sourceDataQuery.GetSort(DataSortBuilder), + Skip = sourceDataQuery.Skip, + Limit = sourceDataQuery.Take, + Options = sourceDataQuery.Options as MongoDbQueryOptions, }; } } diff --git a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs index de1d2ee3..237617fe 100644 --- a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs @@ -28,7 +28,7 @@ internal sealed record DeltaDataDocument : MessageDataDocumentBase private static readonly MessageDataFilterBuilder DataFilterBuilder = new(); - private static readonly MessageSortBuilder SortBuilder = new(); + private static readonly MessageDataSortBuilder DataSortBuilder = new(); public static InsertDocumentsCommand GetInsertCommand ( @@ -61,17 +61,17 @@ Message message public static DocumentQuery GetQuery ( - IMessageDataDataQuery messageDataDataQuery + IMessageDataQuery messageDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = messageDataDataQuery.GetFilter(DataFilterBuilder), - Sort = messageDataDataQuery.GetSort(SortBuilder), - Skip = messageDataDataQuery.Skip, - Limit = messageDataDataQuery.Take, - Options = messageDataDataQuery.Options as MongoDbQueryOptions, + Filter = messageDataQuery.GetFilter(DataFilterBuilder), + Sort = messageDataQuery.GetSort(DataSortBuilder), + Skip = messageDataQuery.Skip, + Limit = messageDataQuery.Take, + Options = messageDataQuery.Options as MongoDbQueryOptions, }; } diff --git a/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs index d4559219..f7de9080 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs @@ -56,17 +56,17 @@ Message message public static DocumentQuery GetQuery ( - ILeaseDataDataQuery leaseDataDataQuery + ILeaseDataQuery leaseDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = leaseDataDataQuery.GetFilter(DataFilterBuilder), - Sort = leaseDataDataQuery.GetSort(DataSortBuilder), - Skip = leaseDataDataQuery.Skip, - Limit = leaseDataDataQuery.Take, - Options = leaseDataDataQuery.Options as MongoDbQueryOptions, + Filter = leaseDataQuery.GetFilter(DataFilterBuilder), + Sort = leaseDataQuery.GetSort(DataSortBuilder), + Skip = leaseDataQuery.Skip, + Limit = leaseDataQuery.Take, + Options = leaseDataQuery.Options as MongoDbQueryOptions, }; } diff --git a/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs index 7f2569e4..1c0c5320 100644 --- a/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs @@ -8,7 +8,7 @@ namespace EntityDb.MongoDb.Documents; internal abstract record SourceDataDocumentBase : DocumentBase, ISourceDataDocument { - protected static readonly SourceDataFilterBuilder FilterBuilder = new(); + protected static readonly SourceDataFilterBuilder DataFilterBuilder = new(); public static ProjectionDefinition StatePointersProjection { get; } = ProjectionBuilder.Include(nameof(StatePointers)); diff --git a/src/EntityDb.MongoDb/Documents/TagDataDocument.cs b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs index 4347bd81..f89a65b3 100644 --- a/src/EntityDb.MongoDb/Documents/TagDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs @@ -54,17 +54,17 @@ Message message public static DocumentQuery GetQuery ( - ITagDataDataQuery tagDataDataQuery + ITagDataQuery tagDataQuery ) { return new DocumentQuery { CollectionName = CollectionName, - Filter = tagDataDataQuery.GetFilter(DataFilterBuilder), - Sort = tagDataDataQuery.GetSort(DataSortBuilder), - Skip = tagDataDataQuery.Skip, - Limit = tagDataDataQuery.Take, - Options = tagDataDataQuery.Options as MongoDbQueryOptions, + Filter = tagDataQuery.GetFilter(DataFilterBuilder), + Sort = tagDataQuery.GetSort(DataSortBuilder), + Skip = tagDataQuery.Skip, + Limit = tagDataQuery.Take, + Options = tagDataQuery.Options as MongoDbQueryOptions, }; } diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs index 24e21f7a..91f6fba5 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -29,117 +29,117 @@ IEnvelopeService envelopeService _envelopeService = envelopeService; } - public IAsyncEnumerable EnumerateSourceIds(ISourceDataDataQuery sourceDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataDataQuery) + .GetQuery(sourceDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataDataQuery) + .GetQuery(messageDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(ILeaseDataDataQuery leaseDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument - .GetQuery(leaseDataDataQuery) + .GetQuery(leaseDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateSourceIds(ITagDataDataQuery tagDataDataQuery, + public IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument - .GetQuery(tagDataDataQuery) + .GetQuery(tagDataQuery) .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ISourceDataDataQuery sourceDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataDataQuery) + .GetQuery(sourceDataQuery) .EnumerateSourceDataStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataDataQuery) + .GetQuery(messageDataQuery) .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ILeaseDataDataQuery leaseDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument - .GetQuery(leaseDataDataQuery) + .GetQuery(leaseDataQuery) .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ITagDataDataQuery tagDataDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument - .GetQuery(tagDataDataQuery) + .GetQuery(tagDataQuery) .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataDataQuery sourceDataDataQuery, + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataDataQuery) + .GetQuery(sourceDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateDeltas(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataDataQuery) + .GetQuery(messageDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateLeases(ILeaseDataDataQuery leaseDataDataQuery, + public IAsyncEnumerable EnumerateLeases(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument - .GetQuery(leaseDataDataQuery) + .GetQuery(leaseDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable EnumerateTags(ITagDataDataQuery tagDataDataQuery, + public IAsyncEnumerable EnumerateTags(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument - .GetQuery(tagDataDataQuery) + .GetQuery(tagDataQuery) .EnumerateData(_mongoSession, _envelopeService, cancellationToken); } public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( - ISourceDataDataQuery sourceDataDataQuery, + ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument - .GetQuery(sourceDataDataQuery) + .GetQuery(sourceDataQuery) .EnumerateEntitiesAnnotation(_mongoSession, _envelopeService, cancellationToken); } - public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataDataQuery messageDataDataQuery, + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument - .GetQuery(messageDataDataQuery) + .GetQuery(messageDataQuery) .EnumerateAnnotatedSourceData(_mongoSession, _envelopeService, cancellationToken); } diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/FilterBuilderBase.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs similarity index 93% rename from src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/FilterBuilderBase.cs rename to src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs index 90ef9cfb..4a010e44 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/FilterBuilderBase.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs @@ -7,7 +7,7 @@ namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal abstract class FilterBuilderBase : IFilterBuilder> +internal abstract class DataFilterBuilderBase : IDataFilterBuilder> { private static readonly FilterDefinitionBuilder FilterBuilder = Builders.Filter; diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs index 26b432f2..819d10db 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs @@ -7,7 +7,7 @@ namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal class MessageDataFilterBuilder : FilterBuilderBase, +internal class MessageDataFilterBuilder : DataFilterBuilderBase, IMessageDataFilterBuilder> { public FilterDefinition StateIdIn(params Id[] stateIds) diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs index 4c51ae6d..0be842e4 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs @@ -6,7 +6,7 @@ namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal sealed class SourceDataFilterBuilder : FilterBuilderBase, +internal sealed class SourceDataFilterBuilder : DataFilterBuilderBase, ISourceDataFilterBuilder> { public FilterDefinition AnyStateIdIn(params Id[] stateIds) diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SortBuilderBase.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/DataSortBuilderBase.cs similarity index 89% rename from src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SortBuilderBase.cs rename to src/EntityDb.MongoDb/Sources/Queries/SortBuilders/DataSortBuilderBase.cs index d8fe17fc..ba161907 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SortBuilderBase.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/DataSortBuilderBase.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal abstract class SortBuilderBase : ISortBuilder> +internal abstract class DataSortBuilderBase : IDataSortBuilder> { private static readonly SortDefinitionBuilder SortBuilder = Builders.Sort; diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs index 556f5f3e..5c14001b 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal sealed class LeaseDataSortBuilder : MessageSortBuilder, ILeaseDataSortBuilder> +internal sealed class LeaseDataSortBuilder : MessageDataSortBuilder, ILeaseDataSortBuilder> { public SortDefinition LeaseScope(bool ascending) { diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageDataSortBuilder.cs similarity index 87% rename from src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs rename to src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageDataSortBuilder.cs index bdba2105..e1729690 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal class MessageSortBuilder : SortBuilderBase, +internal class MessageDataSortBuilder : DataSortBuilderBase, IMessageDataSortBuilder> { public SortDefinition StateId(bool ascending) diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs index afed9b81..930b6d8e 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/SourceDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal sealed class SourceDataSortBuilder : SortBuilderBase, +internal sealed class SourceDataSortBuilder : DataSortBuilderBase, ISourceDataSortBuilder> { public SortDefinition StateIds(bool ascending) diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs index 02c5bf65..03081bc0 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; -internal sealed class TagDataSortBuilder : MessageSortBuilder, ITagDataSortBuilder> +internal sealed class TagDataSortBuilder : MessageDataSortBuilder, ITagDataSortBuilder> { public SortDefinition TagLabel(bool ascending) { diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index cf6cdcab..2b92ca32 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -101,7 +101,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn sourceRepositoryMock .Setup(repository => - repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Returns(() => AsyncEnumerablePolyfill.FromResult(deltas)) .Verifiable(); @@ -143,7 +143,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn sourceRepositoryMock .Verify( - repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), + repository => repository.EnumerateDeltas(It.IsAny(), It.IsAny()), Times.Once); } diff --git a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs index 862c4d4c..7c5b6306 100644 --- a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs +++ b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs @@ -8,9 +8,9 @@ namespace EntityDb.Common.Tests.Extensions; public class FilterBuilderExtensionsTests { - private static IFilterBuilder GetFilterBuilder() + private static IDataFilterBuilder GetFilterBuilder() { - var filterBuilderMock = new Mock>(MockBehavior.Strict); + var filterBuilderMock = new Mock>(MockBehavior.Strict); filterBuilderMock .Setup(builder => builder.Not(It.IsAny())) diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataDataDataDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs similarity index 92% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataDataDataDataQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs index 20258f8f..04075c55 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataDataDataDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record CountDataDataDataDataQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseDataDataQuery, ITagDataDataQuery +public record CountDataQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseDataQuery, ITagDataQuery { public int? Skip => null; diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataDataDataDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs similarity index 91% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataDataDataDataQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs index adeb055b..45ecaf5f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataDataDataDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs @@ -5,8 +5,8 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceIdDataDataDataDataQuery(Id SourceId, object? Options = null) : ISourceDataDataQuery, IMessageDataDataQuery, ILeaseDataDataQuery, - ITagDataDataQuery +public record SourceIdDataQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseDataQuery, + ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataDataDataDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs similarity index 91% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataDataDataDataQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs index 1ae517b8..322001d5 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataDataDataDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs @@ -5,9 +5,9 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceTimeStampDataDataDataDataQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataDataQuery, - IMessageDataDataQuery, - ILeaseDataDataQuery, ITagDataDataQuery +public record SourceTimeStampDataQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataQuery, + IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataDataDataDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs similarity index 90% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataDataDataDataQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs index dbd580bd..5c11ce0e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataDataDataDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs @@ -5,8 +5,8 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record StateDataDataDataDataQuery(Id StateId, object? Options = null) : ISourceDataDataQuery, IMessageDataDataQuery, - ILeaseDataDataQuery, ITagDataDataQuery +public record StateDataQuery(Id StateId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataDataDataDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs similarity index 89% rename from test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataDataDataDataQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs index a5e2f648..543a9632 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataDataDataDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs @@ -5,8 +5,8 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record StateVersionDataDataDataDataQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataDataQuery, - ILeaseDataDataQuery, ITagDataDataQuery +public record StateVersionDataQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) { diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 3c30763e..12151adf 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -108,7 +108,7 @@ bool secondaryPreferred private static async Task TestGetSourceIds ( IServiceScope serviceScope, - ISourceDataDataQuery dataQuery, + ISourceDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -135,7 +135,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetSourceIds ( IServiceScope serviceScope, - IMessageDataDataQuery dataQuery, + IMessageDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -162,7 +162,7 @@ Id[] GetExpectedResults(bool invert) private static async Task TestGetSourceIds ( IServiceScope serviceScope, - ILeaseDataDataQuery dataDataQuery, + ILeaseDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -182,14 +182,14 @@ Id[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateSourceIds(dataDataQuery.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateSourceIds(dataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetSourceIds ( IServiceScope serviceScope, - ITagDataDataQuery dataDataQuery, + ITagDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -209,14 +209,14 @@ Id[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateSourceIds(dataDataQuery.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateSourceIds(dataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetStateIds ( IServiceScope serviceScope, - ISourceDataDataQuery dataQuery, + ISourceDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -245,7 +245,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetStateIds ( IServiceScope serviceScope, - IMessageDataDataQuery dataQuery, + IMessageDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -274,7 +274,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetStateIds ( IServiceScope serviceScope, - ILeaseDataDataQuery dataDataQuery, + ILeaseDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -295,7 +295,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateStatePointers(dataDataQuery.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -303,7 +303,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetStateIds ( IServiceScope serviceScope, - ITagDataDataQuery dataDataQuery, + ITagDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -324,7 +324,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { return sourceRepository - .EnumerateStatePointers(dataDataQuery.Modify(modifiedQueryOptions)) + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) .Select(pointer => pointer.Id); } } @@ -332,7 +332,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetAgentSignatures ( IServiceScope serviceScope, - ISourceDataDataQuery dataQuery, + ISourceDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -359,7 +359,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetDeltas ( IServiceScope serviceScope, - IMessageDataDataQuery dataQuery, + IMessageDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -386,7 +386,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, private static async Task TestGetLeases ( IServiceScope serviceScope, - ILeaseDataDataQuery dataDataQuery, + ILeaseDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -406,14 +406,14 @@ ILease[] GetExpectedResults(bool invert) IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateLeases(dataDataQuery.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateLeases(dataQuery.Modify(modifiedQueryOptions)); } } private static async Task TestGetTags ( IServiceScope serviceScope, - ITagDataDataQuery dataDataQuery, + ITagDataQuery dataQuery, ExpectedObjects expectedObjects ) { @@ -425,7 +425,7 @@ ExpectedObjects expectedObjects IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, ModifiedQueryOptions modifiedQueryOptions) { - return sourceRepository.EnumerateTags(dataDataQuery.Modify(modifiedQueryOptions)); + return sourceRepository.EnumerateTags(dataQuery.Modify(modifiedQueryOptions)); } ITag[] GetExpectedResults(bool invert) @@ -532,13 +532,13 @@ private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenRe .GetRequiredService>() .Create("Count"); - var query = new CountDataDataDataDataQuery(gte, lte, options); + var query = new CountDataQuery(gte, lte, options); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataQuery, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); await TestGetTags(serviceScope, query, expectedObjects); } @@ -815,7 +815,7 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenRet var committed = await writeRepository.Commit(source); - var query = new StateDataDataDataDataQuery(expectedStateId); + var query = new StateDataQuery(expectedStateId); // ARRANGE ASSERTIONS @@ -1064,7 +1064,7 @@ public async Task GivenMessageCommitted_WhenQueryingForVersionOne_ThenReturnTheE var source = CreateSource(new[] { 1ul }); - var versionOneQuery = new StateVersionDataDataDataDataQuery(new Version(1), new Version(1)); + var versionOneQuery = new StateVersionDataQuery(new Version(1), new Version(1)); // ACT @@ -1099,7 +1099,7 @@ public async Task GivenTwoMessagesCommitted_WhenQueryingForVersionTwo_ThenReturn var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); var secondSource = CreateSource(new[] { 2ul }, stateId: stateId); - var versionTwoQuery = new StateVersionDataDataDataDataQuery(new Version(2), new Version(2)); + var versionTwoQuery = new StateVersionDataQuery(new Version(2), new Version(2)); // ACT @@ -1191,17 +1191,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_Then gte.ShouldNotBeNull(); lte.ShouldNotBeNull(); - var query = new SourceTimeStampDataDataDataDataQuery(gte.Value, lte.Value); + var query = new SourceTimeStampDataQuery(gte.Value, lte.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1271,17 +1271,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnE sourceId.ShouldNotBeNull(); - var query = new SourceIdDataDataDataDataQuery(sourceId.Value); + var query = new SourceIdDataQuery(sourceId.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1352,17 +1352,17 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateId_ThenReturnEx stateId.ShouldNotBeNull(); - var query = new StateDataDataDataDataQuery(stateId.Value); + var query = new StateDataQuery(stateId.Value); await PutSources(serviceScope, sources); - await TestGetSourceIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetSourceIds(serviceScope, query as ITagDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ISourceDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as IMessageDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ILeaseDataDataQuery, expectedObjects); - await TestGetStateIds(serviceScope, query as ITagDataDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetSourceIds(serviceScope, query as ITagDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ISourceDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as IMessageDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ILeaseDataQuery, expectedObjects); + await TestGetStateIds(serviceScope, query as ITagDataQuery, expectedObjects); await TestGetAgentSignatures(serviceScope, query, expectedObjects); await TestGetDeltas(serviceScope, query, expectedObjects); await TestGetLeases(serviceScope, query, expectedObjects); @@ -1419,7 +1419,7 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateVersion_ThenRet var sources = new List { source }; - var query = new StateVersionDataDataDataDataQuery(new Version(gte), new Version(lte)); + var query = new StateVersionDataQuery(new Version(gte), new Version(lte)); await PutSources(serviceScope, sources); await TestGetDeltas(serviceScope, query, expectedObjects); diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs index cb801331..c2d85079 100644 --- a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -27,69 +27,69 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti sourceRepositoryMock .Setup(repository => - repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) + repository.EnumerateAgentSignatures(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateLeases(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateLeases(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateTags(It.IsAny(), It.IsAny())) + .Setup(repository => repository.EnumerateTags(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), + repository.EnumerateAnnotatedAgentSignatures(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock .Setup(repository => - repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateAnnotatedDeltas(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock @@ -103,21 +103,21 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti // ACT var sourceIdsFromSourceDataQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(ISourceDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateSourceIds(default(ISourceDataQuery)!).ToArrayAsync(); var sourceIdsFromMessageDataQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageDataQuery)!).ToArrayAsync(); var sourceIdsFromLeaseDataQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseDataQuery)!).ToArrayAsync(); var sourceIdsFromTagDataQuery = - await tryCatchSourceRepository.EnumerateSourceIds(default(ITagDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateSourceIds(default(ITagDataQuery)!).ToArrayAsync(); var statePointersFromSourceDataQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(ISourceDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateStatePointers(default(ISourceDataQuery)!).ToArrayAsync(); var statePointersFromMessageDataQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(IMessageDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateStatePointers(default(IMessageDataQuery)!).ToArrayAsync(); var statePointersFromLeaseDataQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(ILeaseDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateStatePointers(default(ILeaseDataQuery)!).ToArrayAsync(); var statePointersFromTagDataQuery = - await tryCatchSourceRepository.EnumerateStatePointers(default(ITagDataDataQuery)!).ToArrayAsync(); + await tryCatchSourceRepository.EnumerateStatePointers(default(ITagDataQuery)!).ToArrayAsync(); var agentSignatures = await tryCatchSourceRepository.EnumerateAgentSignatures(default!).ToArrayAsync(); var deltas = diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index c66ed23d..f0a98f80 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -36,14 +36,14 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerable.Empty()); // Second query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerable.Empty()); await using var serviceScope = CreateServiceScope(serviceCollection => @@ -145,14 +145,14 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); // First query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerable.Empty()); await using var serviceScope = CreateServiceScope(serviceCollection => @@ -264,14 +264,14 @@ public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenSta sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); // Second query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => - repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(new[] { statePointer })); await using var serviceScope = CreateServiceScope(serviceCollection => diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 1fb45a6f..9e90b76a 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -526,7 +526,7 @@ protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( sourceRepositoryMock .Setup(repository => - repository.EnumerateDeltas(It.IsAny(), It.IsAny())) + repository.EnumerateDeltas(It.IsAny(), It.IsAny())) .Returns(AsyncEnumerablePolyfill.FromResult(deltas)); return GetMockedSourceRepositoryFactory(sourceRepositoryMock, new List()); From 0ca111dfee1066317d16d1aa58e35dccd7667705 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 11 Jan 2024 19:36:27 -0800 Subject: [PATCH 75/93] fix: await in test --- .../Sources/Agents/AgentAccessorTestsBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs b/test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs index 783da059..69363778 100644 --- a/test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs +++ b/test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs @@ -63,7 +63,7 @@ public async Task GivenBackingServiceActive_WhenGettingAgent_ThenReturnAgent() // ACT - var agent = agentAccessor.GetAgent(default!); + var agent = await agentAccessor.GetAgent(default!); // ASSERT From a3939ee6ce967adcd8c8a1e221b9d87cb5d85fa7 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 11 Jan 2024 19:56:52 -0800 Subject: [PATCH 76/93] fix: broken query somehow not caught by tests? will need a regression test --- .../Sources/Queries/Standard/GetDeltasDataQuery.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs index 07ea0744..eb4c144e 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs @@ -13,12 +13,12 @@ public TFilter GetFilter(IMessageDataFilterBuilder builder) { var filters = new List { - builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(StatePointer.Version.Next()), + builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(PersistedStateVersion.Next()), }; - if (PersistedStateVersion != Version.Zero) + if (StatePointer.Version != Version.Zero) { - filters.Add(builder.StateVersionLte(PersistedStateVersion)); + filters.Add(builder.StateVersionLte(StatePointer.Version)); } return builder.And(filters.ToArray()); From 3e8ddc18bc4f40a39841b2cd5b05af8857003ebe Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 11 Jan 2024 20:23:04 -0800 Subject: [PATCH 77/93] chore: sealed/abstract classes --- .../Disposables/DisposableResourceBaseClass.cs | 2 +- .../DisposableResourceBaseRecord.cs | 2 +- .../Entities/EntityRepositoryFactory.cs | 2 +- .../Entities/MultipleEntityRepository.cs | 2 +- .../Projections/ProjectionRepositoryFactory.cs | 2 +- .../Sources/Agents/UnknownAgentSignature.cs | 2 +- .../Annotations/AnnotatedMessageData.cs | 2 +- .../Sources/Annotations/AnnotatedSourceData.cs | 2 +- .../Queues/BufferBlockSourceProcessorQueue.cs | 2 +- .../Queues/SourceProcessorQueueItem.cs | 2 +- .../Queues/TestModeSourceProcessorQueue.cs | 2 +- .../Queries/Modified/ModifiedQueryOptions.cs | 2 +- .../TestModeSourceReprocessorQueue.cs | 2 +- .../Subscribers/EntityStateSourceSubscriber.cs | 2 +- .../ProjectionStateSourceSubscriber.cs | 2 +- .../States/TestModeStateManager.cs | 2 +- .../DefaultPartialTypeResolver.cs | 2 +- .../Converters/EnvelopeHeadersConverter.cs | 2 +- src/EntityDb.Json/Converters/IdConverter.cs | 2 +- .../Converters/VersionConverter.cs | 2 +- .../Commands/DeleteDocumentsCommand.cs | 2 +- .../Commands/InsertDocumentsCommand.cs | 2 +- .../Envelopes/MongoDbEnvelopeService.cs | 2 +- .../Documents/Queries/DocumentQuery.cs | 2 +- .../Serializers/EnvelopeSerializer.cs | 2 +- .../Documents/Serializers/IdSerializer.cs | 2 +- .../Serializers/TimeStampSerializer.cs | 2 +- .../Documents/Serializers/VersionSerializer.cs | 2 +- .../Sources/MongoDbSourceRepository.cs | 2 +- .../Sources/MongoDbSourceRepositoryFactory.cs | 2 +- .../Sources/Sessions/MongoSession.cs | 2 +- .../Sources/Sessions/TestModeMongoSession.cs | 2 +- .../TestModeMongoDbSourceRepositoryFactory.cs | 3 +-- .../States/MongoDbStateRepository.cs | 2 +- .../States/MongoDbStateRepositoryFactory.cs | 2 +- .../States/Sessions/MongoSession.cs | 2 +- .../States/Sessions/TestModeMongoSession.cs | 2 +- .../TestModeMongoDbStateRepositoryFactory.cs | 2 +- .../Agents/HttpContextAgentSignature.cs | 2 +- .../MongoDb/Atlas/Cluster/CreateCollections.cs | 6 +++--- .../MongoDb/Atlas/Cluster/CreateRole.cs | 4 ++-- .../MongoDb/Atlas/Cluster/CreateUser.cs | 4 ++-- .../Cluster/MongoDbAtlasClusterCommand.cs | 2 +- .../MongoDb/Atlas/MongoDbAtlasCommand.cs | 2 +- .../Serverless/CreateCollectionsServerless.cs | 6 +++--- .../MongoDbAtlasServerlessCommand.cs | 2 +- .../Commands/MongoDb/CreateCollections.cs | 4 ++-- .../Commands/MongoDb/MongoDbCommand.cs | 2 +- .../MongoDbAtlas/DigestChallengeRequest.cs | 2 +- .../MongoDbAtlas/Models/Cluster.cs | 2 +- .../MongoDbAtlas/Models/Group.cs | 2 +- .../MongoDbAtlas/Models/ListOf.cs | 2 +- .../Models/MongoDbAtlasResource.cs | 2 +- .../Models/MongoDbAtlasRoleAction.cs | 2 +- .../Models/MongoDbAtlasUserRole.cs | 2 +- .../Models/ServerlessConnectionStrings.cs | 2 +- .../MongoDbAtlas/Models/ServerlessInstance.cs | 2 +- .../MongoDbAtlas/MongoDbAtlasClient.cs | 2 +- .../ConnectionMultiplexerFactory.cs | 2 +- .../States/RedisStateRepository.cs | 2 +- .../States/RedisStateRepositoryFactory.cs | 2 +- .../DatabaseContainerCollection.cs | 4 +--- .../DatabaseContainerFixture.cs | 2 +- .../Entities/EntityRepositoryTests.cs | 2 +- .../Entities/EntityTests.cs | 2 +- .../Extensions/FilterBuilderExtensionsTests.cs | 6 +++--- .../Entities/Deltas/AddLease.cs | 2 +- .../Implementations/Entities/Deltas/AddTag.cs | 2 +- .../Entities/Deltas/DeleteLease.cs | 2 +- .../Entities/Deltas/DeleteTag.cs | 2 +- .../Entities/Deltas/StoreNumber.cs | 2 +- .../Implementations/Entities/TestEntity.cs | 2 +- .../Projections/OneToOneProjection.cs | 2 +- .../Sources/Queries/CountDataQuery.cs | 2 +- .../Sources/Queries/SourceIdDataQuery.cs | 2 +- .../Queries/SourceTimeStampDataQuery.cs | 2 +- .../Sources/Queries/StateDataQuery.cs | 2 +- .../Sources/Queries/StateVersionDataQuery.cs | 2 +- .../States/Attributes/CountLease.cs | 2 +- .../States/Attributes/CountTag.cs | 2 +- .../Projections/ProjectionsTests.cs | 2 +- .../Agents/UnknownAgentAccessorTests.cs | 2 +- .../EntityStateSourceSubscriberTests.cs | 2 +- .../Sources/SourceTests.cs | 2 +- .../Sources/TryCatchSourceRepositoryTests.cs | 2 +- test/EntityDb.Common.Tests/Startup.cs | 2 +- .../States/TryCatchStateRepositoryTests.cs | 2 +- .../Streams/StreamTests.cs | 2 +- test/EntityDb.Common.Tests/TestLogger.cs | 2 +- test/EntityDb.Common.Tests/TestsBase.cs | 18 +++++++++--------- .../TypeResolvers/DefaultTypeResolverTests.cs | 2 +- .../TypeResolvers/LifoTypeResolverTests.cs | 2 +- .../MemberInfoNameTypeResolverTests.cs | 2 +- .../Envelopes/BsonDocumentEnvelopeTests.cs | 2 +- .../Sessions/MongoSessionTests.cs | 2 +- test/EntityDb.MongoDb.Tests/Startup.cs | 2 +- .../Agents/HttpContextAgentAccessorTests.cs | 2 +- .../Agents/HttpContextAgentSignatureTests.cs | 2 +- .../Seeder/HttpContextSeederOptions.cs | 2 +- test/EntityDb.Mvc.Tests/Startup.cs | 2 +- .../Envelopes/JsonElementEnvelopeTests.cs | 2 +- .../Sessions/RedisSessionTests.cs | 2 +- test/EntityDb.Redis.Tests/Startup.cs | 2 +- 103 files changed, 120 insertions(+), 123 deletions(-) diff --git a/src/EntityDb.Common/Disposables/DisposableResourceBaseClass.cs b/src/EntityDb.Common/Disposables/DisposableResourceBaseClass.cs index 084045af..89b9516b 100644 --- a/src/EntityDb.Common/Disposables/DisposableResourceBaseClass.cs +++ b/src/EntityDb.Common/Disposables/DisposableResourceBaseClass.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Disposables; -internal class DisposableResourceBaseClass : IDisposableResource +internal abstract class DisposableResourceBaseClass : IDisposableResource { [ExcludeFromCodeCoverage(Justification = "All Tests Use DisposeAsync")] public virtual void Dispose() diff --git a/src/EntityDb.Common/Disposables/DisposableResourceBaseRecord.cs b/src/EntityDb.Common/Disposables/DisposableResourceBaseRecord.cs index caf4597c..b656f554 100644 --- a/src/EntityDb.Common/Disposables/DisposableResourceBaseRecord.cs +++ b/src/EntityDb.Common/Disposables/DisposableResourceBaseRecord.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Disposables; -internal record DisposableResourceBaseRecord : IDisposableResource +internal abstract record DisposableResourceBaseRecord : IDisposableResource { [ExcludeFromCodeCoverage(Justification = "All Tests Use DisposeAsync")] public virtual void Dispose() diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index 2b2628b2..c3a0ca75 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -6,7 +6,7 @@ namespace EntityDb.Common.Entities; -internal class EntityRepositoryFactory : IEntityRepositoryFactory +internal sealed class EntityRepositoryFactory : IEntityRepositoryFactory where TEntity : IEntity { private readonly IAgentAccessor _agentAccessor; diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index 242f1ea5..3979b04b 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -12,7 +12,7 @@ namespace EntityDb.Common.Entities; -internal class MultipleEntityRepository : DisposableResourceBaseClass, IMultipleEntityRepository +internal sealed class MultipleEntityRepository : DisposableResourceBaseClass, IMultipleEntityRepository where TEntity : IEntity { private readonly IAgent _agent; diff --git a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs index a587fcdd..e68dbede 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Projections; -internal class ProjectionRepositoryFactory : IProjectionRepositoryFactory +internal sealed class ProjectionRepositoryFactory : IProjectionRepositoryFactory where TProjection : IProjection { private readonly IServiceProvider _serviceProvider; diff --git a/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs b/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs index cbd7e719..6407e1f7 100644 --- a/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs +++ b/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs @@ -3,4 +3,4 @@ /// /// Represents the signature of an unknown actor. /// -public record UnknownAgentSignature(Dictionary ApplicationInfo); +public sealed record UnknownAgentSignature(Dictionary ApplicationInfo); diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs index 4a9cf278..d1c68220 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Sources.Annotations; -internal record AnnotatedMessageData +internal sealed record AnnotatedMessageData ( Id SourceId, TimeStamp SourceTimeStamp, diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs index e1dda1de..eeb91e7e 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Sources.Annotations; -internal record AnnotatedSourceData +internal sealed record AnnotatedSourceData ( Id SourceId, TimeStamp SourceTimeStamp, diff --git a/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs index 291afc21..05585d76 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs @@ -7,7 +7,7 @@ namespace EntityDb.Common.Sources.Processors.Queues; [ExcludeFromCodeCoverage(Justification = "Not used in tests.")] -internal class BufferBlockSourceProcessorQueue : BackgroundService, ISourceProcessorQueue +internal sealed class BufferBlockSourceProcessorQueue : BackgroundService, ISourceProcessorQueue { private readonly BufferBlock _bufferBlock = new(); private readonly ILogger _logger; diff --git a/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs index 3cf2c047..20c77377 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueItem.cs @@ -2,7 +2,7 @@ namespace EntityDb.Common.Sources.Processors.Queues; -internal class SourceProcessorQueueItem : ISourceProcessorQueueItem +internal sealed class SourceProcessorQueueItem : ISourceProcessorQueueItem { public required Type SourceProcessorType { get; init; } public required Source Source { get; init; } diff --git a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs index 5114eb58..22504c9e 100644 --- a/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs +++ b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Sources.Processors.Queues; -internal class TestModeSourceProcessorQueue : ISourceProcessorQueue +internal sealed class TestModeSourceProcessorQueue : ISourceProcessorQueue { private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs index 422b7c61..aa564ddc 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs @@ -6,7 +6,7 @@ namespace EntityDb.Common.Sources.Queries.Modified; /// /// Options for modified queries, which can be created via . /// -public record ModifiedQueryOptions +public sealed record ModifiedQueryOptions { /// /// If true, then the new query will return the value of diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs index c882b396..8f15f7cb 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs @@ -4,7 +4,7 @@ namespace EntityDb.Common.Sources.ReprocessorQueues; -internal class TestModeSourceReprocessorQueue : ISourceReprocessorQueue +internal sealed class TestModeSourceReprocessorQueue : ISourceReprocessorQueue { private readonly ILogger _logger; private readonly ISourceProcessorQueue _sourceProcessorQueue; diff --git a/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs index 84a24ca7..b9e4b9c7 100644 --- a/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Subscribers; -internal class EntityStateSourceSubscriber : ISourceSubscriber +internal sealed class EntityStateSourceSubscriber : ISourceSubscriber where TEntity : IEntity { private readonly ISourceProcessorQueue _sourceProcessorQueue; diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs index 47ddb307..7202f4b4 100644 --- a/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Sources.Subscribers; -internal class ProjectionStateSourceSubscriber : ISourceSubscriber +internal sealed class ProjectionStateSourceSubscriber : ISourceSubscriber where TProjection : IProjection { private readonly ISourceProcessorQueue _sourceProcessorQueue; diff --git a/src/EntityDb.Common/States/TestModeStateManager.cs b/src/EntityDb.Common/States/TestModeStateManager.cs index 74544ac7..96f49a12 100644 --- a/src/EntityDb.Common/States/TestModeStateManager.cs +++ b/src/EntityDb.Common/States/TestModeStateManager.cs @@ -4,7 +4,7 @@ namespace EntityDb.Common.States; -internal class TestModeStateManager : DisposableResourceBaseClass +internal sealed class TestModeStateManager : DisposableResourceBaseClass { private readonly Dictionary, List> _dictionary = new(); diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs index e2edfac6..20f8dd9e 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.TypeResolvers; -internal class DefaultPartialTypeResolver : IPartialTypeResolver +internal sealed class DefaultPartialTypeResolver : IPartialTypeResolver { private readonly IOptions _options; diff --git a/src/EntityDb.Json/Converters/EnvelopeHeadersConverter.cs b/src/EntityDb.Json/Converters/EnvelopeHeadersConverter.cs index 79fdc811..d5dfa35c 100644 --- a/src/EntityDb.Json/Converters/EnvelopeHeadersConverter.cs +++ b/src/EntityDb.Json/Converters/EnvelopeHeadersConverter.cs @@ -4,7 +4,7 @@ namespace EntityDb.Json.Converters; -internal class EnvelopeHeadersConverter : JsonConverter +internal sealed class EnvelopeHeadersConverter : JsonConverter { public override EnvelopeHeaders Read ( diff --git a/src/EntityDb.Json/Converters/IdConverter.cs b/src/EntityDb.Json/Converters/IdConverter.cs index 2da79af2..d28a697c 100644 --- a/src/EntityDb.Json/Converters/IdConverter.cs +++ b/src/EntityDb.Json/Converters/IdConverter.cs @@ -4,7 +4,7 @@ namespace EntityDb.Json.Converters; -internal class IdConverter : JsonConverter +internal sealed class IdConverter : JsonConverter { public override Id Read ( diff --git a/src/EntityDb.Json/Converters/VersionConverter.cs b/src/EntityDb.Json/Converters/VersionConverter.cs index 201bddea..b73b9bb5 100644 --- a/src/EntityDb.Json/Converters/VersionConverter.cs +++ b/src/EntityDb.Json/Converters/VersionConverter.cs @@ -4,7 +4,7 @@ namespace EntityDb.Json.Converters; -internal class VersionConverter : JsonConverter +internal sealed class VersionConverter : JsonConverter { public override Version Read ( diff --git a/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs index e47d747c..54ef6771 100644 --- a/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs @@ -4,7 +4,7 @@ namespace EntityDb.MongoDb.Documents.Commands; -internal record DeleteDocumentsCommand +internal sealed record DeleteDocumentsCommand { public required string CollectionName { get; init; } public required FilterDefinition FilterDefinition { get; init; } diff --git a/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs index 4a3b26c0..4a5467ea 100644 --- a/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs @@ -2,7 +2,7 @@ namespace EntityDb.MongoDb.Documents.Commands; -internal record InsertDocumentsCommand +internal sealed record InsertDocumentsCommand ( string CollectionName, TDocument[] Documents diff --git a/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs index c61b80f2..7c4b7305 100644 --- a/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs +++ b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs @@ -11,7 +11,7 @@ namespace EntityDb.MongoDb.Documents.Envelopes; -internal class MongoDbEnvelopeService : IEnvelopeService +internal sealed class MongoDbEnvelopeService : IEnvelopeService { public const string TypeDiscriminatorPropertyName = "_t"; diff --git a/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs index 38548108..87beef37 100644 --- a/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Documents.Queries; -internal record DocumentQuery +internal sealed record DocumentQuery { public required string CollectionName { get; init; } public required FilterDefinition Filter { get; init; } diff --git a/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs index b6890820..a6b1683c 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs @@ -6,7 +6,7 @@ namespace EntityDb.MongoDb.Documents.Serializers; -internal class EnvelopeSerializer : IBsonSerializer> +internal sealed class EnvelopeSerializer : IBsonSerializer> { private static readonly BsonDocumentSerializer BsonDocumentSerializer = new(); diff --git a/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs index 92e5b27d..33b34794 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs @@ -3,7 +3,7 @@ namespace EntityDb.MongoDb.Documents.Serializers; -internal class IdSerializer : IBsonSerializer +internal sealed class IdSerializer : IBsonSerializer { public Type ValueType => typeof(Id); diff --git a/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs index a9c1e48e..4e1b49ef 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs @@ -3,7 +3,7 @@ namespace EntityDb.MongoDb.Documents.Serializers; -internal class TimeStampSerializer : IBsonSerializer +internal sealed class TimeStampSerializer : IBsonSerializer { public Type ValueType => typeof(TimeStamp); diff --git a/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs index c955da49..b1f8f732 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs @@ -3,7 +3,7 @@ namespace EntityDb.MongoDb.Documents.Serializers; -internal class VersionSerializer : IBsonSerializer +internal sealed class VersionSerializer : IBsonSerializer { public Type ValueType => typeof(Version); diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs index 91f6fba5..8071ef7c 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -14,7 +14,7 @@ namespace EntityDb.MongoDb.Sources; -internal class MongoDbSourceRepository : DisposableResourceBaseClass, ISourceRepository +internal sealed class MongoDbSourceRepository : DisposableResourceBaseClass, ISourceRepository { private readonly IEnvelopeService _envelopeService; private readonly IMongoSession _mongoSession; diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs index 75ac4e9b..68b87bc0 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs @@ -9,7 +9,7 @@ namespace EntityDb.MongoDb.Sources; -internal class MongoDbSourceRepositoryFactory : DisposableResourceBaseClass, IMongoDbSourceRepositoryFactory +internal sealed class MongoDbSourceRepositoryFactory : DisposableResourceBaseClass, IMongoDbSourceRepositoryFactory { private readonly IEnvelopeService _envelopeService; private readonly IOptionsFactory _optionsFactory; diff --git a/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs index 19d5976c..9733b3b6 100644 --- a/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs @@ -10,7 +10,7 @@ namespace EntityDb.MongoDb.Sources.Sessions; -internal record MongoSession +internal sealed record MongoSession ( ILogger Logger, IMongoDatabase MongoDatabase, diff --git a/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs index 6b1e7779..247fab83 100644 --- a/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.Sources.Sessions; -internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession +internal sealed record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession { public IMongoDatabase MongoDatabase => MongoSession.MongoDatabase; diff --git a/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs index 3f5c684b..74e0b86e 100644 --- a/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs @@ -2,8 +2,7 @@ namespace EntityDb.MongoDb.Sources; -internal class - TestModeMongoDbSourceRepositoryFactory : MongoDbSourceRepositoryFactoryWrapper +internal sealed class TestModeMongoDbSourceRepositoryFactory : MongoDbSourceRepositoryFactoryWrapper { private (IMongoSession Normal, TestModeMongoSession TestMode)? _sessions; diff --git a/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs index 19b4d446..ef39b534 100644 --- a/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs @@ -8,7 +8,7 @@ namespace EntityDb.MongoDb.States; -internal class MongoDbStateRepository : DisposableResourceBaseClass, IStateRepository +internal sealed class MongoDbStateRepository : DisposableResourceBaseClass, IStateRepository where TState : notnull { private readonly IEnvelopeService _envelopeService; diff --git a/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs index 634dfc59..b0230e92 100644 --- a/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs @@ -9,7 +9,7 @@ namespace EntityDb.MongoDb.States; -internal class MongoDbStateRepositoryFactory : DisposableResourceBaseClass, +internal sealed class MongoDbStateRepositoryFactory : DisposableResourceBaseClass, IMongoDbStateRepositoryFactory where TState : notnull { diff --git a/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs index e88acb73..48d524e4 100644 --- a/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs @@ -9,7 +9,7 @@ namespace EntityDb.MongoDb.States.Sessions; -internal record MongoSession +internal sealed record MongoSession ( ILogger Logger, IMongoDatabase MongoDatabase, diff --git a/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs index 20029f34..50885d68 100644 --- a/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs @@ -5,7 +5,7 @@ namespace EntityDb.MongoDb.States.Sessions; -internal record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession +internal sealed record TestModeMongoSession(IMongoSession MongoSession) : DisposableResourceBaseRecord, IMongoSession { public string CollectionName => MongoSession.CollectionName; diff --git a/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs b/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs index 97ce05b3..44638988 100644 --- a/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs @@ -2,7 +2,7 @@ namespace EntityDb.MongoDb.States; -internal class TestModeMongoDbStateRepositoryFactory : MongoDbStateRepositoryFactoryWrapper +internal sealed class TestModeMongoDbStateRepositoryFactory : MongoDbStateRepositoryFactoryWrapper { private (IMongoSession Normal, TestModeMongoSession TestMode)? _sessions; diff --git a/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs b/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs index 6aa3f985..c9ec0987 100644 --- a/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs +++ b/src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs @@ -7,7 +7,7 @@ namespace EntityDb.Mvc.Agents; /// Represents the description of an agent who records sources using an /// . /// -public class HttpContextAgentSignature +public sealed class HttpContextAgentSignature { /// /// Request details diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs index 43a4c53d..16f2f8f6 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs @@ -5,7 +5,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class CreateCollections : CommandBase +internal sealed class CreateCollections : CommandBase { private static async Task Execute(Arguments arguments) { @@ -32,7 +32,7 @@ private static async Task Execute(Arguments arguments) await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } - protected static void AddClusterNameArgument(Command command) + private static void AddClusterNameArgument(Command command) { var clusterNameArgument = new Argument("cluster-name") { @@ -57,7 +57,7 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createCollections); } - public record Arguments + public sealed record Arguments ( string GroupName, string PublicKey, diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs index f4dace0f..221b0d68 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs @@ -5,7 +5,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class CreateRole : CommandBase +internal sealed class CreateRole : CommandBase { private static async Task Execute(Arguments arguments) { @@ -84,7 +84,7 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createRole); } - public record Arguments + public sealed record Arguments ( string GroupName, string PublicKey, diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs index bb517894..1265a225 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs @@ -4,7 +4,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class CreateUser : CommandBase +internal sealed class CreateUser : CommandBase { private static async Task Execute(Arguments arguments) { @@ -55,7 +55,7 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createUser); } - public record Arguments + public sealed record Arguments ( string GroupName, string PublicKey, diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/MongoDbAtlasClusterCommand.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/MongoDbAtlasClusterCommand.cs index 3fca573d..2ffb669a 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/MongoDbAtlasClusterCommand.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/MongoDbAtlasClusterCommand.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class MongoDbAtlasClusterCommand +internal sealed class MongoDbAtlasClusterCommand { public static void AddTo(Command parentCommand) { diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/MongoDbAtlasCommand.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/MongoDbAtlasCommand.cs index ab4e912b..ddcd318c 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/MongoDbAtlasCommand.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/MongoDbAtlasCommand.cs @@ -4,7 +4,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas; -internal class MongoDbAtlasCommand +internal sealed class MongoDbAtlasCommand { public static void AddTo(Command parentCommand) { diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs index c22776a3..2e44704c 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs @@ -5,7 +5,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Serverless; -internal class CreateCollectionsServerless : CommandBase +internal sealed class CreateCollectionsServerless : CommandBase { private static async Task Execute(Arguments arguments) { @@ -32,7 +32,7 @@ private static async Task Execute(Arguments arguments) await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } - protected static void AddServerlessNameArgument(Command command) + private static void AddServerlessNameArgument(Command command) { var clusterNameArgument = new Argument("instance-name") { @@ -57,7 +57,7 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createCollections); } - public record Arguments + public sealed record Arguments ( string GroupName, string PublicKey, diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs index 847b71fb..97564f73 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Serverless; -internal class MongoDbAtlasServerlessCommand +internal sealed class MongoDbAtlasServerlessCommand { public static void AddTo(Command parentCommand) { diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs index c5055f39..1c223dab 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs @@ -5,7 +5,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb; -internal class CreateCollections : CommandBase +internal sealed class CreateCollections : CommandBase { private static async Task Execute(Arguments arguments) { @@ -37,7 +37,7 @@ public static void AddTo(Command parentCommand) parentCommand.AddCommand(createCollectionsDirect); } - public record Arguments + public sealed record Arguments ( string ServiceName, string ConnectionString diff --git a/src/EntityDb.Provisioner/Commands/MongoDb/MongoDbCommand.cs b/src/EntityDb.Provisioner/Commands/MongoDb/MongoDbCommand.cs index 8050b125..fce39474 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/MongoDbCommand.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/MongoDbCommand.cs @@ -3,7 +3,7 @@ namespace EntityDb.Provisioner.Commands.MongoDb; -internal class MongoDbCommand +internal sealed class MongoDbCommand { public static void AddTo(Command parentCommand) { diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs b/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs index 36acbde8..59098a60 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs @@ -6,7 +6,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas; -internal record DigestChallengeRequest(string? Realm, string? Domain, string? Nonce, string? Algorithm, string? Qop, +internal sealed record DigestChallengeRequest(string? Realm, string? Domain, string? Nonce, string? Algorithm, string? Qop, string? Stale, uint NonceCount, string ClientNonce, DateTime ExpiresAt) { private const int MinClientNonce = 0x100000; diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/Cluster.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/Cluster.cs index aa48bffb..83976d3e 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/Cluster.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/Cluster.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class Cluster +internal sealed class Cluster { [JsonPropertyName("srvAddress")] public string? SrvAddress { get; set; } } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/Group.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/Group.cs index fd29d010..6dd2b8ba 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/Group.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/Group.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class Group +internal sealed class Group { [JsonPropertyName("id")] public string Id { get; set; } = ""; diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ListOf.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ListOf.cs index 22ba7edb..d116bb12 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ListOf.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ListOf.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class ListOf +internal sealed class ListOf { [JsonPropertyName("results")] public T[] Results { get; set; } = Array.Empty(); diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs index fac64e36..5902c639 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class MongoDbAtlasResource +internal sealed class MongoDbAtlasResource { [JsonPropertyName("db")] public string? Db { get; set; } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs index 6472d97d..c677748e 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class MongoDbAtlasRoleAction +internal sealed class MongoDbAtlasRoleAction { [JsonPropertyName("action")] public string? Action { get; set; } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs index 731db300..6c51666a 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class MongoDbAtlasUserRole +internal sealed class MongoDbAtlasUserRole { [JsonPropertyName("databaseName")] public string? DatabaseName { get; set; } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessConnectionStrings.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessConnectionStrings.cs index d8e18904..5d1a7e7e 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessConnectionStrings.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessConnectionStrings.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class ServerlessConnectionStrings +internal sealed class ServerlessConnectionStrings { [JsonPropertyName("standardSrv")] public string? StandardSrv { get; set; } } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs index 4208230b..5e8b6620 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs @@ -2,7 +2,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class ServerlessInstance +internal sealed class ServerlessInstance { [JsonPropertyName("connectionStrings")] public ServerlessConnectionStrings? ConnectionStrings { get; set; } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs b/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs index 8756fcc3..0ae853ab 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/MongoDbAtlasClient.cs @@ -6,7 +6,7 @@ namespace EntityDb.Provisioner.MongoDbAtlas; -internal class MongoDbAtlasClient : IDisposable +internal sealed class MongoDbAtlasClient : IDisposable { private static readonly HttpClient HttpClient = new(); diff --git a/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs b/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs index 87ad8189..7913526c 100644 --- a/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs +++ b/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs @@ -4,7 +4,7 @@ namespace EntityDb.Redis.ConnectionMultiplexers; -internal class ConnectionMultiplexerFactory : DisposableResourceBaseClass +internal sealed class ConnectionMultiplexerFactory : DisposableResourceBaseClass { private readonly ConcurrentDictionary _connectionMultiplexers = new(); private readonly SemaphoreSlim _connectionSemaphore = new(1); diff --git a/src/EntityDb.Redis/States/RedisStateRepository.cs b/src/EntityDb.Redis/States/RedisStateRepository.cs index 2a0ab9ac..96ca4c26 100644 --- a/src/EntityDb.Redis/States/RedisStateRepository.cs +++ b/src/EntityDb.Redis/States/RedisStateRepository.cs @@ -6,7 +6,7 @@ namespace EntityDb.Redis.States; -internal class RedisStateRepository : DisposableResourceBaseClass, IStateRepository +internal sealed class RedisStateRepository : DisposableResourceBaseClass, IStateRepository { private readonly IEnvelopeService _envelopeService; private readonly IRedisSession _redisSession; diff --git a/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs b/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs index 2fdbc30a..7f184a23 100644 --- a/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs +++ b/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs @@ -8,7 +8,7 @@ namespace EntityDb.Redis.States; -internal class RedisStateRepositoryFactory : DisposableResourceBaseClass, +internal sealed class RedisStateRepositoryFactory : DisposableResourceBaseClass, IStateRepositoryFactory { private readonly ConnectionMultiplexerFactory _connectionMultiplexerFactory; diff --git a/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs b/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs index 7b760a2e..8b997c6d 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerCollection.cs @@ -3,6 +3,4 @@ namespace EntityDb.Common.Tests; [CollectionDefinition(nameof(DatabaseContainerCollection))] -public class DatabaseContainerCollection : ICollectionFixture -{ -} +public sealed class DatabaseContainerCollection : ICollectionFixture; diff --git a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs index 028e554d..ed6147df 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs @@ -4,7 +4,7 @@ namespace EntityDb.Common.Tests; -public class DatabaseContainerFixture : IAsyncLifetime +public sealed class DatabaseContainerFixture : IAsyncLifetime { public const string OmniParameter = "entitydb"; diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs index f4f4c57a..49d9fdb5 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -14,7 +14,7 @@ namespace EntityDb.Common.Tests.Entities; [Collection(nameof(DatabaseContainerCollection))] [SuppressMessage("ReSharper", "UnusedMember.Local")] -public class EntityRepositoryTests : TestsBase +public sealed class EntityRepositoryTests : TestsBase { public EntityRepositoryTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base( diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 2b92ca32..b580f9ae 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -20,7 +20,7 @@ namespace EntityDb.Common.Tests.Entities; [Collection(nameof(DatabaseContainerCollection))] [SuppressMessage("ReSharper", "UnusedMember.Local")] -public class EntityTests : TestsBase +public sealed class EntityTests : TestsBase { public EntityTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base( serviceProvider, databaseContainerFixture) diff --git a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs index 7c5b6306..4b4534be 100644 --- a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs +++ b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs @@ -6,7 +6,7 @@ namespace EntityDb.Common.Tests.Extensions; -public class FilterBuilderExtensionsTests +public sealed class FilterBuilderExtensionsTests { private static IDataFilterBuilder GetFilterBuilder() { @@ -194,7 +194,7 @@ public void XnorTruthTable() } } - private record SingleInput(bool Input1, bool ExpectedOutput); + private sealed record SingleInput(bool Input1, bool ExpectedOutput); - private record DoubleInput(bool Input1, bool Input2, bool ExpectedOutput); + private sealed record DoubleInput(bool Input1, bool Input2, bool ExpectedOutput); } diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs index 3e699a05..6e9be7a9 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; -public record AddLease(ILease Lease) : DoNothing, IAddLeasesDelta +public sealed record AddLease(ILease Lease) : DoNothing, IAddLeasesDelta { public IEnumerable GetLeases(TestEntity state) { diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs index 248f56bf..b0c1e408 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; -public record AddTag(ITag Tag) : DoNothing, IAddTagsDelta +public sealed record AddTag(ITag Tag) : DoNothing, IAddTagsDelta { public IEnumerable GetTags(TestEntity state) { diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs index 8498c22f..db2e3e87 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; -public record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesDelta +public sealed record DeleteLease(ILease Lease) : DoNothing, IDeleteLeasesDelta { public IEnumerable GetLeases(TestEntity state) { diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs index a06d882b..551ec8b1 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; -public record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsDelta +public sealed record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsDelta { public IEnumerable GetTags(TestEntity state) { diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs index 299ffa3b..7f206df3 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs @@ -6,7 +6,7 @@ namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; -public record StoreNumber(ulong Number) : IReducer, IMutator, +public sealed record StoreNumber(ulong Number) : IReducer, IMutator, IAddLeasesDelta, IAddTagsDelta { public IEnumerable GetLeases(TestEntity state) diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 53d7aab6..8d80234f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -6,7 +6,7 @@ namespace EntityDb.Common.Tests.Implementations.Entities; -public record TestEntity : IEntity, IStateWithTestLogic +public sealed record TestEntity : IEntity, IStateWithTestLogic { public required Pointer Pointer { get; init; } diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 8bb3cc7e..fd175958 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -11,7 +11,7 @@ namespace EntityDb.Common.Tests.Implementations.Projections; -public class OneToOneProjection : IProjection, IStateWithTestLogic +public sealed class OneToOneProjection : IProjection, IStateWithTestLogic { public TimeStamp LastSourceAt { get; set; } diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs index 04075c55..478ccf69 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record CountDataQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseDataQuery, ITagDataQuery +public sealed record CountDataQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseDataQuery, ITagDataQuery { public int? Skip => null; diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs index 45ecaf5f..8f402c15 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceIdDataQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseDataQuery, +public sealed record SourceIdDataQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs index 322001d5..4c1edc7f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record SourceTimeStampDataQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataQuery, +public sealed record SourceTimeStampDataQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseDataQuery, ITagDataQuery { diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs index 5c11ce0e..d9425a7e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record StateDataQuery(Id StateId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, +public sealed record StateDataQuery(Id StateId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs index 543a9632..f7b0726e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public record StateVersionDataQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataQuery, +public sealed record StateVersionDataQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataQuery, ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) diff --git a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs index b3e2a7a9..f9a25e42 100644 --- a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs @@ -2,7 +2,7 @@ namespace EntityDb.Common.Tests.Implementations.States.Attributes; -public record CountLease(ulong Number) : ILease +public sealed record CountLease(ulong Number) : ILease { public string Scope => ""; public string Label => ""; diff --git a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs index 30df1708..3f1bd790 100644 --- a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs @@ -2,7 +2,7 @@ namespace EntityDb.Common.Tests.Implementations.States.Attributes; -public record CountTag(ulong Number) : ITag +public sealed record CountTag(ulong Number) : ITag { public string Label => ""; public string Value => $"{Number}"; diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index 8f1025ea..0eb81479 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -13,7 +13,7 @@ namespace EntityDb.Common.Tests.Projections; [Collection(nameof(DatabaseContainerCollection))] [SuppressMessage("ReSharper", "UnusedMember.Local")] -public class ProjectionsTests : TestsBase +public sealed class ProjectionsTests : TestsBase { public ProjectionsTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) diff --git a/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs b/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs index 4cef9c51..8e48e5c3 100644 --- a/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs +++ b/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Tests.Sources.Agents; -public class UnknownAgentAccessorTests : AgentAccessorTestsBase +public sealed class UnknownAgentAccessorTests : AgentAccessorTestsBase { public UnknownAgentAccessorTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs index 18b150f5..c177999c 100644 --- a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs +++ b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs @@ -10,7 +10,7 @@ namespace EntityDb.Common.Tests.Sources; [Collection(nameof(DatabaseContainerCollection))] [SuppressMessage("ReSharper", "UnusedMember.Local")] -public class EntityStateSourceSubscriberTests : TestsBase +public sealed class EntityStateSourceSubscriberTests : TestsBase { public EntityStateSourceSubscriberTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index 12151adf..ab5dfbae 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -1439,7 +1439,7 @@ public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObj ); } - private class ExpectedObjects + private sealed class ExpectedObjects { public readonly List FalseAgentSignatures = new(); public readonly List FalseDeltas = new(); diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs index c2d85079..3c145497 100644 --- a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -8,7 +8,7 @@ namespace EntityDb.Common.Tests.Sources; -public class TryCatchSourceRepositoryTests : TestsBase +public sealed class TryCatchSourceRepositoryTests : TestsBase { public TryCatchSourceRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) { diff --git a/test/EntityDb.Common.Tests/Startup.cs b/test/EntityDb.Common.Tests/Startup.cs index 2d95e9ca..d52984fe 100644 --- a/test/EntityDb.Common.Tests/Startup.cs +++ b/test/EntityDb.Common.Tests/Startup.cs @@ -1,5 +1,5 @@ namespace EntityDb.Common.Tests; -public class Startup : StartupBase +public sealed class Startup : StartupBase { } diff --git a/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs index 828ab058..b637bc7b 100644 --- a/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs @@ -13,7 +13,7 @@ namespace EntityDb.Common.Tests.States; [SuppressMessage("ReSharper", "UnusedMember.Local")] -public class TryCatchStateRepositoryTests : TestsBase +public sealed class TryCatchStateRepositoryTests : TestsBase { public TryCatchStateRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) { diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index f0a98f80..a7ecd1da 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -15,7 +15,7 @@ namespace EntityDb.Common.Tests.Streams; [Collection(nameof(DatabaseContainerCollection))] -public class StreamTests : TestsBase +public sealed class StreamTests : TestsBase { public StreamTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : base(serviceProvider, databaseContainerFixture) diff --git a/test/EntityDb.Common.Tests/TestLogger.cs b/test/EntityDb.Common.Tests/TestLogger.cs index 59f8c83f..a48dc982 100644 --- a/test/EntityDb.Common.Tests/TestLogger.cs +++ b/test/EntityDb.Common.Tests/TestLogger.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Tests; -public class TestLogger : ILogger +public sealed class TestLogger : ILogger { private readonly ILogger _logger; private readonly ITest _test; diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 9e90b76a..57494f1e 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -33,7 +33,7 @@ namespace EntityDb.Common.Tests; -public class TestsBase +public abstract class TestsBase where TStartup : IStartup, new() { public delegate void AddDependenciesDelegate(IServiceCollection serviceCollection); @@ -560,7 +560,7 @@ protected static IStateRepositoryFactory GetMockedStateRepositoryFactor return stateRepositoryFactoryMock.Object; } - public record Log + public sealed record Log { public required object[] ScopesStates { get; init; } public required LogLevel LogLevel { get; init; } @@ -569,7 +569,7 @@ public record Log public required Exception? Exception { get; init; } } - private class Logger(ICollection logs) : ILogger + private sealed class Logger(ICollection logs) : ILogger { private readonly Stack _scopeStates = new(); @@ -598,7 +598,7 @@ void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Excep }); } - private class Scope(Stack scopeStates) : IDisposable + private sealed class Scope(Stack scopeStates) : IDisposable { void IDisposable.Dispose() { @@ -607,7 +607,7 @@ void IDisposable.Dispose() } } - internal record TestServiceScope : DisposableResourceBaseRecord, IServiceScope + internal sealed record TestServiceScope : DisposableResourceBaseRecord, IServiceScope { public required ServiceProvider SingletonServiceProvider { get; init; } public required AsyncServiceScope AsyncServiceScope { get; init; } @@ -621,7 +621,7 @@ public override async ValueTask DisposeAsync() } } - public record SourceRepositoryAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, + public sealed record SourceRepositoryAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, AddDependenciesDelegate AddDependencies) { public override string ToString() @@ -630,7 +630,7 @@ public override string ToString() } } - public record StateRepositoryAdder(string Name, Type StateType, AddDependenciesDelegate AddDependencies) + public sealed record StateRepositoryAdder(string Name, Type StateType, AddDependenciesDelegate AddDependencies) { public override string ToString() { @@ -638,7 +638,7 @@ public override string ToString() } } - public record EntityRepositoryAdder(string Name, Type EntityType, AddDependenciesDelegate AddDependencies) + public sealed record EntityRepositoryAdder(string Name, Type EntityType, AddDependenciesDelegate AddDependencies) { public override string ToString() { @@ -646,7 +646,7 @@ public override string ToString() } } - public record ProjectionRepositoryAdder(string Name, Type ProjectionType, AddDependenciesDelegate AddDependencies) + public sealed record ProjectionRepositoryAdder(string Name, Type ProjectionType, AddDependenciesDelegate AddDependencies) { public override string ToString() { diff --git a/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs index fbb2747b..67c1a3dc 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/DefaultTypeResolverTests.cs @@ -7,7 +7,7 @@ namespace EntityDb.Common.Tests.TypeResolvers; -public class DefaultTypeResolverTests +public sealed class DefaultTypeResolverTests { [Fact] public void GivenEmptyHeaders_WhenLoadingType_ThenReturnNull() diff --git a/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs index 165f8235..6562793f 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/LifoTypeResolverTests.cs @@ -11,7 +11,7 @@ namespace EntityDb.Common.Tests.TypeResolvers; -public class LifoTypeResolverTests : TestsBase +public sealed class LifoTypeResolverTests : TestsBase { public LifoTypeResolverTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs b/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs index 31649a77..6bb7c64e 100644 --- a/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs +++ b/test/EntityDb.Common.Tests/TypeResolvers/MemberInfoNameTypeResolverTests.cs @@ -5,7 +5,7 @@ namespace EntityDb.Common.Tests.TypeResolvers; -public class MemberInfoNameTypeResolverTests +public sealed class MemberInfoNameTypeResolverTests { [Fact] public void GivenMemberInfoNameTypeResolverKnowsExpectedType_WhenResolvingType_ThenReturnExpectedType() diff --git a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs index b5dff9a2..1e798157 100644 --- a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs +++ b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs @@ -10,7 +10,7 @@ namespace EntityDb.MongoDb.Tests.Envelopes; -public class BsonDocumentEnvelopeTests : EnvelopeTestsBase +public sealed class BsonDocumentEnvelopeTests : EnvelopeTestsBase { public BsonDocumentEnvelopeTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs index 2fa28e15..d550a4ef 100644 --- a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs +++ b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs @@ -6,7 +6,7 @@ namespace EntityDb.MongoDb.Tests.Sessions; -public class MongoSessionTests : TestsBase +public sealed class MongoSessionTests : TestsBase { public MongoSessionTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.MongoDb.Tests/Startup.cs b/test/EntityDb.MongoDb.Tests/Startup.cs index 6e0818ad..71e3a6d8 100644 --- a/test/EntityDb.MongoDb.Tests/Startup.cs +++ b/test/EntityDb.MongoDb.Tests/Startup.cs @@ -4,7 +4,7 @@ namespace EntityDb.MongoDb.Tests; -public class Startup : StartupBase +public sealed class Startup : StartupBase { public override void AddServices(IServiceCollection serviceCollection) { diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs index 7142502b..99572c8f 100644 --- a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs +++ b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs @@ -7,7 +7,7 @@ namespace EntityDb.Mvc.Tests.Agents; -public class HttpContextAgentAccessorTests : AgentAccessorTestsBase +public sealed class HttpContextAgentAccessorTests : AgentAccessorTestsBase { public HttpContextAgentAccessorTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs index 04e8b248..e36d035c 100644 --- a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs +++ b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs @@ -5,7 +5,7 @@ namespace EntityDb.Mvc.Tests.Agents; -public class HttpContextAgentSignatureTests +public sealed class HttpContextAgentSignatureTests { [Fact] public void GivenNoRedactedHeaders_WhenHttpContextHasHeader_ThenAgentSignatureHasHeaderValue() diff --git a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs index c8c319ba..f2ef8cda 100644 --- a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs +++ b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs @@ -1,6 +1,6 @@ namespace EntityDb.Mvc.Tests.Seeder; -public class HttpContextSeederOptions +public sealed class HttpContextSeederOptions { // Request public Dictionary Headers { get; init; } = new(); diff --git a/test/EntityDb.Mvc.Tests/Startup.cs b/test/EntityDb.Mvc.Tests/Startup.cs index 62a83457..820e534a 100644 --- a/test/EntityDb.Mvc.Tests/Startup.cs +++ b/test/EntityDb.Mvc.Tests/Startup.cs @@ -4,7 +4,7 @@ namespace EntityDb.Mvc.Tests; -public class Startup : StartupBase +public sealed class Startup : StartupBase { public override void AddServices(IServiceCollection serviceCollection) { diff --git a/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs b/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs index 0e5f6166..0be5620e 100644 --- a/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs +++ b/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs @@ -3,7 +3,7 @@ namespace EntityDb.Redis.Tests.Envelopes; -public class JsonElementEnvelopeTests : EnvelopeTestsBase +public sealed class JsonElementEnvelopeTests : EnvelopeTestsBase { public JsonElementEnvelopeTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs index 1473ae8f..c9dc3aaa 100644 --- a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs +++ b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs @@ -6,7 +6,7 @@ namespace EntityDb.Redis.Tests.Sessions; -public class RedisSessionTests : TestsBase +public sealed class RedisSessionTests : TestsBase { public RedisSessionTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { diff --git a/test/EntityDb.Redis.Tests/Startup.cs b/test/EntityDb.Redis.Tests/Startup.cs index 966339b6..420394e8 100644 --- a/test/EntityDb.Redis.Tests/Startup.cs +++ b/test/EntityDb.Redis.Tests/Startup.cs @@ -4,7 +4,7 @@ namespace EntityDb.Redis.Tests; -public class Startup : StartupBase +public sealed class Startup : StartupBase { public override void AddServices(IServiceCollection serviceCollection) { From 3e4a706abcb9c0045fb26ae96f2c73c6a3ca475c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 11 Jan 2024 21:10:11 -0800 Subject: [PATCH 78/93] fix: don't access agent until commit --- .../Entities/EntityRepositoryFactory.cs | 17 +++----- .../Entities/MultipleEntityRepository.cs | 43 +++++-------------- .../Streams/MultipleStreamRepository.cs | 16 ++++--- .../Streams/StreamRepositoryFactory.cs | 13 +++--- 4 files changed, 33 insertions(+), 56 deletions(-) diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index c3a0ca75..ac53db13 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -10,19 +10,16 @@ internal sealed class EntityRepositoryFactory : IEntityRepositoryFactor where TEntity : IEntity { private readonly IAgentAccessor _agentAccessor; - private readonly IServiceProvider _serviceProvider; private readonly ISourceRepositoryFactory _sourceRepositoryFactory; private readonly IStateRepositoryFactory? _stateRepositoryFactory; public EntityRepositoryFactory ( - IServiceProvider serviceProvider, IAgentAccessor agentAccessor, ISourceRepositoryFactory sourceRepositoryFactory, IStateRepositoryFactory? stateRepositoryFactory = null ) { - _serviceProvider = serviceProvider; _agentAccessor = agentAccessor; _sourceRepositoryFactory = sourceRepositoryFactory; _stateRepositoryFactory = stateRepositoryFactory; @@ -70,17 +67,15 @@ public async Task> CreateMultiple CancellationToken cancellationToken = default ) { - var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); - var sourceRepository = await _sourceRepositoryFactory .Create(sourceSessionOptionsName, cancellationToken); if (_stateRepositoryFactory is null || stateSessionOptionsName is null) { - return MultipleEntityRepository.Create + return new MultipleEntityRepository ( - _serviceProvider, - agent, + _agentAccessor, + agentSignatureOptionsName, sourceRepository ); } @@ -88,10 +83,10 @@ public async Task> CreateMultiple var stateRepository = await _stateRepositoryFactory .Create(stateSessionOptionsName, cancellationToken); - return MultipleEntityRepository.Create + return new MultipleEntityRepository ( - _serviceProvider, - agent, + _agentAccessor, + agentSignatureOptionsName, sourceRepository, stateRepository ); diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index 3979b04b..1b07e079 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -15,18 +15,22 @@ namespace EntityDb.Common.Entities; internal sealed class MultipleEntityRepository : DisposableResourceBaseClass, IMultipleEntityRepository where TEntity : IEntity { - private readonly IAgent _agent; + private readonly string _agentSignatureOptionsName; + private readonly IAgentAccessor _agentAccessor; private readonly Dictionary _knownEntities = new(); private readonly List _messages = new(); public MultipleEntityRepository ( - IAgent agent, + IAgentAccessor agentAccessor, + string agentSignatureOptionsName, ISourceRepository sourceRepository, IStateRepository? stateRepository = null ) { - _agent = agent; + _agentSignatureOptionsName = agentSignatureOptionsName; + _agentAccessor = agentAccessor; + SourceRepository = sourceRepository; StateRepository = stateRepository; } @@ -126,12 +130,14 @@ public async Task Commit(CancellationToken cancellationToken = default) { return true; } + + var agent = await _agentAccessor.GetAgent(_agentSignatureOptionsName, cancellationToken); var source = new Source { Id = Id.NewId(), - TimeStamp = _agent.TimeStamp, - AgentSignature = _agent.Signature, + TimeStamp = agent.TimeStamp, + AgentSignature = agent.Signature, Messages = _messages.ToArray(), }; @@ -156,31 +162,4 @@ public override async ValueTask DisposeAsync() await StateRepository.DisposeAsync(); } } - - public static MultipleEntityRepository Create - ( - IServiceProvider serviceProvider, - IAgent agent, - ISourceRepository sourceRepository, - IStateRepository? stateRepository = null - ) - { - if (stateRepository is null) - { - return ActivatorUtilities.CreateInstance> - ( - serviceProvider, - agent, - sourceRepository - ); - } - - return ActivatorUtilities.CreateInstance> - ( - serviceProvider, - agent, - sourceRepository, - stateRepository - ); - } } diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 98f27c07..5ecd6172 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -15,14 +15,16 @@ internal sealed class MultipleStreamRepository : DisposableResourceBaseClass, IM private const string RootScope = "Stream"; private const string StreamKeyLabel = "StreamKey"; private const string MessageKeyLabel = "MessageKey"; - private readonly IAgent _agent; + private readonly string _agentSignatureOptionsName; + private readonly IAgentAccessor _agentAccessor; private readonly Dictionary _knownStreams = new(); private readonly List _messages = new(); - public MultipleStreamRepository(IAgent agent, ISourceRepository sourceRepository) + public MultipleStreamRepository(IAgentAccessor agentAccessor, string agentSignatureOptionsName, ISourceRepository sourceRepository) { - _agent = agent; - + _agentSignatureOptionsName = agentSignatureOptionsName; + _agentAccessor = agentAccessor; + SourceRepository = sourceRepository; } @@ -86,12 +88,14 @@ public async Task Commit(CancellationToken cancellationToken = default) { return true; } + + var agent = await _agentAccessor.GetAgent(_agentSignatureOptionsName, cancellationToken); var source = new Source { Id = Id.NewId(), - TimeStamp = _agent.TimeStamp, - AgentSignature = _agent.Signature, + TimeStamp = agent.TimeStamp, + AgentSignature = agent.Signature, Messages = _messages.ToArray(), }; diff --git a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs index 7d8bd031..448fe9f6 100644 --- a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs +++ b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs @@ -2,24 +2,20 @@ using EntityDb.Abstractions.Sources.Agents; using EntityDb.Abstractions.Streams; using EntityDb.Abstractions.ValueObjects; -using Microsoft.Extensions.DependencyInjection; namespace EntityDb.Common.Streams; internal sealed class StreamRepositoryFactory : IStreamRepositoryFactory { private readonly IAgentAccessor _agentAccessor; - private readonly IServiceProvider _serviceProvider; private readonly ISourceRepositoryFactory _sourceRepositoryFactory; public StreamRepositoryFactory ( - IServiceProvider serviceProvider, IAgentAccessor agentAccessor, ISourceRepositoryFactory sourceRepositoryFactory ) { - _serviceProvider = serviceProvider; _agentAccessor = agentAccessor; _sourceRepositoryFactory = sourceRepositoryFactory; } @@ -47,11 +43,14 @@ public async Task CreateMultiple CancellationToken cancellationToken ) { - var agent = await _agentAccessor.GetAgent(agentSignatureOptionsName, cancellationToken); - var sourceRepository = await _sourceRepositoryFactory .Create(sourceSessionOptionsName, cancellationToken); - return ActivatorUtilities.CreateInstance(_serviceProvider, agent, sourceRepository); + return new MultipleStreamRepository + ( + _agentAccessor, + agentSignatureOptionsName, + sourceRepository + ); } } From 5f27bf523e83e04fa93c9f71a39c8721b72e7fc1 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 11:34:42 -0800 Subject: [PATCH 79/93] feat: more stream capabilities 1. Load - requires existing stream 2. Create - assumes a new stream 3. First message for Create is always Version.One so that optimistic concurrency exceptions get logged 4. Rename Stage to Append to be consistent 5. Allow Append without Message Key 6. Allow Deltas to manage leases/tags --- .../Properties/AssemblyInfo.cs | 4 + src/EntityDb.Abstractions/Sources/Message.cs | 31 +++++++ .../Streams/IMultipleStreamRepository.cs | 8 +- src/EntityDb.Abstractions/Streams/IStream.cs | 9 ++ .../Streams/IStreamRepositoryFactory.cs | 16 ++++ .../ValueObjects/Version.cs | 3 + .../Entities/MultipleEntityRepository.cs | 19 +--- .../Exceptions/UnknownStreamException.cs | 2 +- .../Streams/MultipleStreamRepository.cs | 87 +++++++++++++++++-- .../Streams/SingleEntityRepository.cs | 2 +- src/EntityDb.Common/Streams/Stream.cs | 3 +- .../Streams/StreamRepositoryFactory.cs | 32 +++++++ .../Streams/StreamTests.cs | 16 ++-- 13 files changed, 195 insertions(+), 37 deletions(-) create mode 100644 src/EntityDb.Abstractions/Properties/AssemblyInfo.cs create mode 100644 src/EntityDb.Abstractions/Streams/IStream.cs diff --git a/src/EntityDb.Abstractions/Properties/AssemblyInfo.cs b/src/EntityDb.Abstractions/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..48ae5cf3 --- /dev/null +++ b/src/EntityDb.Abstractions/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +// src +[assembly: InternalsVisibleTo("EntityDb.Common")] diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index aacf43dd..44a84a02 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -1,4 +1,5 @@ using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Sources; @@ -42,4 +43,34 @@ public sealed record Message /// The tags to be deleted. /// public ITag[] DeleteTags { get; init; } = Array.Empty(); + + internal static Message NewMessage + ( + TState state, + Pointer statePointer, + object delta, + IEnumerable? additionalAddLeases = null + ) + { + additionalAddLeases ??= Array.Empty(); + + return new Message + { + Id = Id.NewId(), + StatePointer = statePointer, + Delta = delta, + AddLeases = delta is IAddLeasesDelta addLeasesDelta + ? addLeasesDelta.GetLeases(state).Concat(additionalAddLeases).ToArray() + : Array.Empty(), + AddTags = delta is IAddTagsDelta addTagsDelta + ? addTagsDelta.GetTags(state).ToArray() + : Array.Empty(), + DeleteLeases = delta is IDeleteLeasesDelta deleteLeasesDelta + ? deleteLeasesDelta.GetLeases(state).ToArray() + : Array.Empty(), + DeleteTags = delta is IDeleteTagsDelta deleteTagsDelta + ? deleteTagsDelta.GetTags(state).ToArray() + : Array.Empty(), + }; + } } diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs index 8119452c..616dfc91 100644 --- a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -14,6 +14,10 @@ public interface IMultipleStreamRepository : IDisposableResource /// ISourceRepository SourceRepository { get; } + void Create(Key streamKey, CancellationToken cancellationToken = default); + + Task Load(Key streamKey, CancellationToken cancellationToken = default); + /// /// Load a stream if it exists, or create a new stream. /// @@ -22,6 +26,8 @@ public interface IMultipleStreamRepository : IDisposableResource /// A task. Task LoadOrCreate(Key streamKey, CancellationToken cancellationToken = default); + void Append(Key streamKey, object delta, CancellationToken cancellationToken = default); + /// /// Stages a single delta with a given state key and message key. /// @@ -30,7 +36,7 @@ public interface IMultipleStreamRepository : IDisposableResource /// The new delta that modifies the stream. /// A cancellation token. /// Only false if the message key already exists. - Task Stage(Key streamKey, Key messageKey, object delta, CancellationToken cancellationToken = default); + Task Append(Key streamKey, Key messageKey, object delta, CancellationToken cancellationToken = default); /// /// Commits all stages deltas. diff --git a/src/EntityDb.Abstractions/Streams/IStream.cs b/src/EntityDb.Abstractions/Streams/IStream.cs new file mode 100644 index 00000000..36ddee44 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/IStream.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.ValueObjects; + +namespace EntityDb.Abstractions.Streams; + +public interface IStream +{ + Key Key { get; } + Id Id { get; } +} diff --git a/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs index 34b1efc1..54a1e583 100644 --- a/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs @@ -24,6 +24,22 @@ Task CreateSingle string sourceSessionOptionsName, CancellationToken cancellationToken = default ); + + Task CreateSingleForNew + ( + Key streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ); + + Task CreateSingleForExisting + ( + Key streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ); /// /// Creates a new instance of . diff --git a/src/EntityDb.Abstractions/ValueObjects/Version.cs b/src/EntityDb.Abstractions/ValueObjects/Version.cs index 240c327c..a2369700 100644 --- a/src/EntityDb.Abstractions/ValueObjects/Version.cs +++ b/src/EntityDb.Abstractions/ValueObjects/Version.cs @@ -17,6 +17,9 @@ public readonly record struct Version(ulong Value) /// this value is reserved to point to the latest state. /// public static readonly Version Zero = new(ulong.MinValue); + + + public static readonly Version One = new(1); /// /// Returns the next version. diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index 1b07e079..c2842e5b 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -102,24 +102,7 @@ public void Append(Id entityId, object delta) entity = entity.Reduce(delta); - _messages.Add(new Message - { - Id = Id.NewId(), - StatePointer = entity.GetPointer(), - Delta = delta, - AddLeases = delta is IAddLeasesDelta addLeasesDelta - ? addLeasesDelta.GetLeases(entity).ToArray() - : Array.Empty(), - AddTags = delta is IAddTagsDelta addTagsDelta - ? addTagsDelta.GetTags(entity).ToArray() - : Array.Empty(), - DeleteLeases = delta is IDeleteLeasesDelta deleteLeasesDelta - ? deleteLeasesDelta.GetLeases(entity).ToArray() - : Array.Empty(), - DeleteTags = delta is IDeleteTagsDelta deleteTagsDelta - ? deleteTagsDelta.GetTags(entity).ToArray() - : Array.Empty(), - }); + _messages.Add(Message.NewMessage(entity, entity.GetPointer(), delta)); _knownEntities[entityId] = entity; } diff --git a/src/EntityDb.Common/Exceptions/UnknownStreamException.cs b/src/EntityDb.Common/Exceptions/UnknownStreamException.cs index 7574982b..336750d1 100644 --- a/src/EntityDb.Common/Exceptions/UnknownStreamException.cs +++ b/src/EntityDb.Common/Exceptions/UnknownStreamException.cs @@ -3,7 +3,7 @@ namespace EntityDb.Common.Exceptions; /// -/// The exception that is thrown when +/// The exception that is thrown when /// is called for an stream that is not loaded into the repository. /// public sealed class UnknownStreamException : Exception; diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 5ecd6172..498d8182 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -1,12 +1,14 @@ using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.States.Deltas; using EntityDb.Abstractions.Streams; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Queries.Standard; using EntityDb.Common.States.Attributes; +using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Streams; @@ -48,7 +50,73 @@ public async Task LoadOrCreate(Key streamKey, CancellationToken cancellationToke _knownStreams.Add(streamKey, stream); } - public async Task Stage(Key streamKey, Key messageKey, object delta, + public async Task Load(Key streamKey, CancellationToken cancellationToken = default) + { + if (_knownStreams.ContainsKey(streamKey)) + { + throw new ExistingStreamException(); + } + + var streamKeyLease = GetStreamKeyLease(streamKey); + + var streamPointer = await GetStreamPointer(streamKeyLease, cancellationToken); + + if (streamPointer == default) + { + throw new UnknownStreamException(); + } + + _knownStreams.Add(streamKey, new Stream + { + Key = streamKey, + Id = streamPointer.Id, + New = false, + }); + } + + public void Create(Key streamKey, CancellationToken cancellationToken = default) + { + if (_knownStreams.ContainsKey(streamKey)) + { + throw new ExistingStreamException(); + } + + _knownStreams.Add(streamKey, new Stream + { + Key = streamKey, + Id = Id.NewId(), + New = true, + }); + } + + public void Append(Key streamKey, object delta, CancellationToken cancellationToken = default) + { + if (!_knownStreams.TryGetValue(streamKey, out var stream)) + { + throw new UnknownStreamException(); + } + + var addLeases = new List(); + + Pointer nextStreamPointer; + + if (stream.New) + { + addLeases.Add(GetStreamKeyLease(stream.Key)); + + _knownStreams[streamKey] = stream with { New = false }; + + nextStreamPointer = stream.Id + Version.One; + } + else + { + nextStreamPointer = stream.Id; + } + + _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, addLeases)); + } + + public async Task Append(Key streamKey, Key messageKey, object delta, CancellationToken cancellationToken = default) { if (!_knownStreams.TryGetValue(streamKey, out var stream)) @@ -58,26 +126,31 @@ public async Task Stage(Key streamKey, Key messageKey, object delta, var messageKeyLease = GetMessageKeyLease(streamKey, messageKey); - var statePointer = await GetStreamPointer(messageKeyLease, cancellationToken); + var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); - if (statePointer != default) + if (streamPointer != default) { return false; } var addLeases = new List { messageKeyLease }; + Pointer nextStreamPointer; + if (stream.New) { addLeases.Add(GetStreamKeyLease(stream.Key)); _knownStreams[streamKey] = stream with { New = false }; - } - _messages.Add(new Message + nextStreamPointer = stream.Id + Version.One; + } + else { - Id = Id.NewId(), StatePointer = stream.Id, Delta = delta, AddLeases = addLeases.ToArray(), - }); + nextStreamPointer = stream.Id; + } + + _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, addLeases)); return true; } diff --git a/src/EntityDb.Common/Streams/SingleEntityRepository.cs b/src/EntityDb.Common/Streams/SingleEntityRepository.cs index 09491c95..3f2e473b 100644 --- a/src/EntityDb.Common/Streams/SingleEntityRepository.cs +++ b/src/EntityDb.Common/Streams/SingleEntityRepository.cs @@ -21,7 +21,7 @@ public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository public Task Stage(Key messageKey, object delta, CancellationToken cancellationToken = default) { - return _multipleStreamRepository.Stage(StreamKey, messageKey, delta, cancellationToken); + return _multipleStreamRepository.Append(StreamKey, messageKey, delta, cancellationToken); } public Task Commit(CancellationToken cancellationToken = default) diff --git a/src/EntityDb.Common/Streams/Stream.cs b/src/EntityDb.Common/Streams/Stream.cs index 71575732..8fd072aa 100644 --- a/src/EntityDb.Common/Streams/Stream.cs +++ b/src/EntityDb.Common/Streams/Stream.cs @@ -1,8 +1,9 @@ +using EntityDb.Abstractions.Streams; using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Streams; -internal sealed record Stream +internal sealed record Stream : IStream { public required Key Key { get; init; } public required Id Id { get; init; } diff --git a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs index 448fe9f6..1a1c263e 100644 --- a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs +++ b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs @@ -36,6 +36,38 @@ public async Task CreateSingle return new SingleStreamRepository(multipleStreamRepository, streamKey); } + public async Task CreateSingleForNew + ( + Key streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ) + { + var multipleStreamRepository = + await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, cancellationToken); + + multipleStreamRepository.Create(streamKey, cancellationToken); + + return new SingleStreamRepository(multipleStreamRepository, streamKey); + } + + public async Task CreateSingleForExisting + ( + Key streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ) + { + var multipleStreamRepository = + await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, cancellationToken); + + await multipleStreamRepository.Load(streamKey, cancellationToken); + + return new SingleStreamRepository(multipleStreamRepository, streamKey); + } + public async Task CreateMultiple ( string agentSignatureOptionsName, diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index a7ecd1da..f18c6a21 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -68,7 +68,7 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc await writeRepository.LoadOrCreate(streamKey); var staged = await writeRepository - .Stage(streamKey, messageKey, expectedDelta); + .Append(streamKey, messageKey, expectedDelta); var committed = await writeRepository .Commit(); @@ -114,7 +114,7 @@ public async Task GivenNewStream_WhenStagingNewMessageKey_ThenCommitReturnsTrue( await writeRepository.LoadOrCreate(streamKey); var staged = await writeRepository - .Stage(streamKey, messageKey, new DoNothing()); + .Append(streamKey, messageKey, new DoNothing()); var committed = await writeRepository.Commit(); @@ -178,7 +178,7 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted await writeRepository.LoadOrCreate(streamKey); var staged = await writeRepository - .Stage(streamKey, messageKey, new DoNothing()); + .Append(streamKey, messageKey, new DoNothing()); var committed = await writeRepository.Commit(); @@ -223,7 +223,7 @@ public async Task GivenExistingStream_WhenStagingNewMessageKey_ThenCommitReturns await writeRepository.LoadOrCreate(streamKey); var firstStaged = await writeRepository - .Stage(streamKey, messageKey1, new DoNothing()); + .Append(streamKey, messageKey1, new DoNothing()); var firstCommitted = await writeRepository.Commit(); @@ -235,7 +235,7 @@ public async Task GivenExistingStream_WhenStagingNewMessageKey_ThenCommitReturns // ACT var secondStaged = await writeRepository - .Stage(streamKey, messageKey2, new DoNothing()); + .Append(streamKey, messageKey2, new DoNothing()); var secondCommitted = await writeRepository.Commit(); @@ -291,7 +291,7 @@ public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenSta // ACT var staged = await writeRepository - .Stage(streamKey, messageKey, new DoNothing()); + .Append(streamKey, messageKey, new DoNothing()); // ASSERT @@ -320,7 +320,7 @@ public async Task GivenExistingStream_WhenStagingDuplicateMessageKey_ThenStagedR await writeRepository.LoadOrCreate(streamKey); var stagedOnce = await writeRepository - .Stage(streamKey, messageKey, new DoNothing()); + .Append(streamKey, messageKey, new DoNothing()); var committedOnce = await writeRepository.Commit(); @@ -332,7 +332,7 @@ public async Task GivenExistingStream_WhenStagingDuplicateMessageKey_ThenStagedR // ACT var stagedTwice = await writeRepository - .Stage(streamKey, messageKey, new DoNothing()); + .Append(streamKey, messageKey, new DoNothing()); // ASSERT From a6b7c919170d6bbdac96fd9c54c1544eea5fb50e Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 11:49:31 -0800 Subject: [PATCH 80/93] refactor: change from array to read-only collection --- src/EntityDb.Abstractions/Sources/Message.cs | 12 ++++++------ .../Sources/MongoDbSourceRepository.cs | 8 ++++---- .../Entities/EntityRepositoryTests.cs | 2 +- test/EntityDb.Common.Tests/Streams/StreamTests.cs | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index 44a84a02..9cb79258 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -27,29 +27,29 @@ public sealed record Message /// /// The leases to be added. /// - public ILease[] AddLeases { get; init; } = Array.Empty(); + public IReadOnlyCollection AddLeases { get; init; } = Array.Empty(); /// /// The tags to be added. /// - public ITag[] AddTags { get; init; } = Array.Empty(); + public IReadOnlyCollection AddTags { get; init; } = Array.Empty(); /// /// The leases to be deleted. /// - public ILease[] DeleteLeases { get; init; } = Array.Empty(); + public IReadOnlyCollection DeleteLeases { get; init; } = Array.Empty(); /// /// The tags to be deleted. /// - public ITag[] DeleteTags { get; init; } = Array.Empty(); + public IReadOnlyCollection DeleteTags { get; init; } = Array.Empty(); internal static Message NewMessage ( TState state, Pointer statePointer, object delta, - IEnumerable? additionalAddLeases = null + IReadOnlyCollection? additionalAddLeases = null ) { additionalAddLeases ??= Array.Empty(); @@ -61,7 +61,7 @@ internal static Message NewMessage Delta = delta, AddLeases = delta is IAddLeasesDelta addLeasesDelta ? addLeasesDelta.GetLeases(state).Concat(additionalAddLeases).ToArray() - : Array.Empty(), + : additionalAddLeases, AddTags = delta is IAddTagsDelta addTagsDelta ? addTagsDelta.GetTags(state).ToArray() : Array.Empty(), diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs index 8071ef7c..116c7ca6 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -210,28 +210,28 @@ await DeltaDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); - if (message.AddLeases.Length > 0) + if (message.AddLeases.Count > 0) { await LeaseDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); } - if (message.AddTags.Length > 0) + if (message.AddTags.Count > 0) { await TagDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); } - if (message.DeleteLeases.Length > 0) + if (message.DeleteLeases.Count > 0) { await LeaseDataDocument .GetDeleteCommand(message) .Execute(_mongoSession, cancellationToken); } - if (message.DeleteTags.Length > 0) + if (message.DeleteTags.Count > 0) { await TagDataDocument .GetDeleteCommand(message) diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs index 49d9fdb5..a9baeffd 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -107,7 +107,7 @@ private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_Th committedSources.Count.ShouldBe(1); committedSources[0].Messages.Length.ShouldBe(1); - committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); + committedSources[0].Messages[0].AddLeases.Count.ShouldBe(1); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index f18c6a21..ee850be2 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -82,9 +82,9 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc committedSources[0].Messages.Length.ShouldBe(1); committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.Zero); committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); - committedSources[0].Messages[0].AddLeases.Length.ShouldBe(2); - committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedMessageKeyLease); - committedSources[0].Messages[0].AddLeases[1].ShouldBe(expectedStreamKeyLease); + committedSources[0].Messages[0].AddLeases.Count.ShouldBe(2); + committedSources[0].Messages[0].AddLeases.ElementAt(0).ShouldBe(expectedMessageKeyLease); + committedSources[0].Messages[0].AddLeases.ElementAt(1).ShouldBe(expectedStreamKeyLease); } [Theory] @@ -191,8 +191,8 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted committedSources[0].Messages.Length.ShouldBe(1); committedSources[0].Messages[0].StatePointer.Id.ShouldBe(statePointer.Id); committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.Zero); - committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); - committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedLease); + committedSources[0].Messages[0].AddLeases.Count.ShouldBe(1); + committedSources[0].Messages[0].AddLeases.ElementAt(0).ShouldBe(expectedLease); } [Theory] From e4ecfccdce7b8af151a0171440be7c8ba5241fb4 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 11:49:46 -0800 Subject: [PATCH 81/93] chore: change exception name to be similar to the onef or entity --- ...knownStreamException.cs => UnknownStreamKeyException.cs} | 2 +- src/EntityDb.Common/Streams/MultipleStreamRepository.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/EntityDb.Common/Exceptions/{UnknownStreamException.cs => UnknownStreamKeyException.cs} (81%) diff --git a/src/EntityDb.Common/Exceptions/UnknownStreamException.cs b/src/EntityDb.Common/Exceptions/UnknownStreamKeyException.cs similarity index 81% rename from src/EntityDb.Common/Exceptions/UnknownStreamException.cs rename to src/EntityDb.Common/Exceptions/UnknownStreamKeyException.cs index 336750d1..117c8df4 100644 --- a/src/EntityDb.Common/Exceptions/UnknownStreamException.cs +++ b/src/EntityDb.Common/Exceptions/UnknownStreamKeyException.cs @@ -6,4 +6,4 @@ namespace EntityDb.Common.Exceptions; /// The exception that is thrown when /// is called for an stream that is not loaded into the repository. /// -public sealed class UnknownStreamException : Exception; +public sealed class UnknownStreamKeyException : Exception; diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 498d8182..4a1031f4 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -63,7 +63,7 @@ public async Task Load(Key streamKey, CancellationToken cancellationToken = defa if (streamPointer == default) { - throw new UnknownStreamException(); + throw new UnknownStreamKeyException(); } _knownStreams.Add(streamKey, new Stream @@ -93,7 +93,7 @@ public void Append(Key streamKey, object delta, CancellationToken cancellationTo { if (!_knownStreams.TryGetValue(streamKey, out var stream)) { - throw new UnknownStreamException(); + throw new UnknownStreamKeyException(); } var addLeases = new List(); @@ -121,7 +121,7 @@ public async Task Append(Key streamKey, Key messageKey, object delta, { if (!_knownStreams.TryGetValue(streamKey, out var stream)) { - throw new UnknownStreamException(); + throw new UnknownStreamKeyException(); } var messageKeyLease = GetMessageKeyLease(streamKey, messageKey); From 1d3e97aec95fda84292d4ac199038e87969beb2a Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 11:49:52 -0800 Subject: [PATCH 82/93] expect version zero --- test/EntityDb.Common.Tests/Streams/StreamTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index ee850be2..49c17e0e 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -80,7 +80,7 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc committedSources.Count.ShouldBe(1); committedSources[0].Messages.Length.ShouldBe(1); - committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.Zero); + committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.One); committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); committedSources[0].Messages[0].AddLeases.Count.ShouldBe(2); committedSources[0].Messages[0].AddLeases.ElementAt(0).ShouldBe(expectedMessageKeyLease); From 65ea3c59329903f089d4fe19a6cdfdd2a80870ae Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 12:02:29 -0800 Subject: [PATCH 83/93] fix: Create is void, not async --- src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs | 2 +- src/EntityDb.Common/Streams/MultipleStreamRepository.cs | 2 +- src/EntityDb.Common/Streams/StreamRepositoryFactory.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs index 616dfc91..3efb7a92 100644 --- a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -14,7 +14,7 @@ public interface IMultipleStreamRepository : IDisposableResource /// ISourceRepository SourceRepository { get; } - void Create(Key streamKey, CancellationToken cancellationToken = default); + void Create(Key streamKey); Task Load(Key streamKey, CancellationToken cancellationToken = default); diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 4a1031f4..6afd71bc 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -74,7 +74,7 @@ public async Task Load(Key streamKey, CancellationToken cancellationToken = defa }); } - public void Create(Key streamKey, CancellationToken cancellationToken = default) + public void Create(Key streamKey) { if (_knownStreams.ContainsKey(streamKey)) { diff --git a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs index 1a1c263e..7db804f7 100644 --- a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs +++ b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs @@ -47,7 +47,7 @@ public async Task CreateSingleForNew var multipleStreamRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, cancellationToken); - multipleStreamRepository.Create(streamKey, cancellationToken); + multipleStreamRepository.Create(streamKey); return new SingleStreamRepository(multipleStreamRepository, streamKey); } From f1cab068e585eec0c3bc9f9bd612307b0ac09f6f Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 12:04:24 -0800 Subject: [PATCH 84/93] fix: this Append is void, not async --- src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs | 2 +- src/EntityDb.Common/Streams/MultipleStreamRepository.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs index 3efb7a92..1e6da3e1 100644 --- a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -26,7 +26,7 @@ public interface IMultipleStreamRepository : IDisposableResource /// A task. Task LoadOrCreate(Key streamKey, CancellationToken cancellationToken = default); - void Append(Key streamKey, object delta, CancellationToken cancellationToken = default); + void Append(Key streamKey, object delta); /// /// Stages a single delta with a given state key and message key. diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 6afd71bc..1be0f0f3 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -89,7 +89,7 @@ public void Create(Key streamKey) }); } - public void Append(Key streamKey, object delta, CancellationToken cancellationToken = default) + public void Append(Key streamKey, object delta) { if (!_knownStreams.TryGetValue(streamKey, out var stream)) { From c77e5fa3e914752e33de893a8b9e0a82211c7154 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 17:07:35 -0800 Subject: [PATCH 85/93] refactor 1. attributes belong to sources, not states 2. move TimeStamp from ValueObjects to Sources 3. move Pointer and Version to states, prefix with State 4. move Id to top level abstraction 5. don't pass message key directly to Append, check if delta implements IAddMessageKeyDelta 6. refactor Message.NewMessage to handle all lease collection (entities in the future might have key support?) 7. move Key to common, replace with IStateKey and IMessageKey in abstractions 8. move pointer satisfied check to the exception it always ends up throwing if fall --- .../Entities/IEntityRepositoryFactory.cs | 6 +- .../Entities/IMultipleEntityRepository.cs | 5 +- .../Entities/ISingleEntityRepository.cs | 5 +- .../Extensions/EnumerableExtensions.cs | 14 ++ src/EntityDb.Abstractions/Id.cs | 45 +++++++ .../Projections/IProjection.cs | 9 +- .../Projections/IProjectionRepository.cs | 7 +- .../Sources/Agents/IAgent.cs | 4 +- .../Annotations/IAnnotatedMessageData.cs | 10 +- .../Annotations/IAnnotatedSourceData.cs | 12 +- .../{States => Sources}/Attributes/ILease.cs | 2 +- .../Sources/Attributes/IMessageKey.cs | 6 + .../Sources/Attributes/IStateKey.cs | 6 + .../{States => Sources}/Attributes/ITag.cs | 2 +- .../Sources/ISourceRepository.cs | 14 +- src/EntityDb.Abstractions/Sources/Message.cs | 57 ++++++--- .../FilterBuilders/IDataFilterBuilder.cs | 4 +- .../FilterBuilders/ILeaseDataFilterBuilder.cs | 2 +- .../IMessageDataFilterBuilder.cs | 15 +-- .../ISourceDataFilterBuilder.cs | 4 +- .../FilterBuilders/ITagDataFilterBuilder.cs | 2 +- .../SortBuilders/ILeaseDataSortBuilder.cs | 2 +- .../SortBuilders/ITagDataSortBuilder.cs | 2 +- src/EntityDb.Abstractions/Sources/Source.cs | 4 +- .../{ValueObjects => Sources}/TimeStamp.cs | 2 +- .../Deltas/IAddAlternateStateKeysDelta.cs | 8 ++ .../States/Deltas/IAddLeasesDelta.cs | 2 +- .../States/Deltas/IAddMessageKeyDelta.cs | 8 ++ .../States/Deltas/IAddTagsDelta.cs | 2 +- .../States/Deltas/IDeleteLeasesDelta.cs | 2 +- .../States/Deltas/IDeleteTagsDelta.cs | 2 +- src/EntityDb.Abstractions/States/IState.cs | 12 +- .../States/IStateRepository.cs | 13 +- .../States/StatePointer.cs | 28 ++++ .../States/StateVersion.cs | 35 +++++ .../Streams/IMultipleStreamRepository.cs | 26 ++-- .../Streams/ISingleStreamRepository.cs | 16 ++- src/EntityDb.Abstractions/Streams/IStream.cs | 4 +- .../Streams/IStreamRepositoryFactory.cs | 22 ++-- src/EntityDb.Abstractions/ValueObjects/Id.cs | 43 ------- src/EntityDb.Abstractions/ValueObjects/Key.cs | 14 -- .../ValueObjects/Pointer.cs | 48 ------- .../ValueObjects/Version.cs | 38 ------ .../Entities/EntityRepositoryFactory.cs | 4 +- .../Entities/MultipleEntityRepository.cs | 25 ++-- .../Entities/SingleEntityRepository.cs | 6 +- .../OptimisticConcurrencyException.cs | 21 ++- .../Exceptions/StateDoesNotExistException.cs | 27 +++- .../Extensions/EnumerableExtensions.cs | 18 +++ .../Projections/ProjectionRepository.cs | 8 +- .../Sources/Agents/StandardAgent.cs | 4 +- .../Annotations/AnnotatedMessageData.cs | 10 +- .../Annotations/AnnotatedSourceData.cs | 10 +- src/EntityDb.Common/Sources/Attributes/Key.cs | 32 +++++ .../Sources/Attributes/Lease.cs | 6 + src/EntityDb.Common/Sources/Attributes/Tag.cs | 6 + .../Sources/Documents/DocumentsExtensions.cs | 17 +-- .../Sources/Documents/IDocument.cs | 3 +- .../Sources/Documents/IMessageDataDocument.cs | 5 +- .../Sources/Documents/ISourceDataDocument.cs | 5 +- .../Processors/EntityStateSourceProcessor.cs | 5 +- .../Queries/Modified/ModifiedQueryOptions.cs | 6 +- .../Sources/Queries/QueryExtensions.cs | 11 +- .../Queries/Standard/DeleteLeasesDataQuery.cs | 4 +- .../Queries/Standard/DeleteTagsDataQuery.cs | 4 +- .../Queries/Standard/GetDeltasDataQuery.cs | 11 +- .../Standard/GetLastStateVersionDataQuery.cs | 4 +- .../Queries/Standard/GetSourceDataQuery.cs | 4 +- .../Standard/MatchingLeaseDataQuery.cs | 2 +- .../Standard/MatchingLeasesDataQuery.cs | 2 +- .../Sources/SourceRepositoryExtensions.cs | 4 +- .../Sources/SourceRepositoryWrapper.cs | 15 ++- .../States/Attributes/Lease.cs | 6 - src/EntityDb.Common/States/Attributes/Tag.cs | 6 - .../States/StateRepositoryWrapper.cs | 7 +- .../States/TestModeStateManager.cs | 11 +- .../States/TestModeStateRepository.cs | 7 +- .../Streams/MultipleStreamRepository.cs | 121 +++++------------- ...epository.cs => SingleStreamRepository.cs} | 10 +- src/EntityDb.Common/Streams/Stream.cs | 9 +- .../Streams/StreamRepositoryFactory.cs | 8 +- src/EntityDb.Json/Converters/IdConverter.cs | 2 +- .../Converters/VersionConverter.cs | 12 +- .../Documents/DeltaDataDocument.cs | 10 +- .../Documents/DocumentBase.cs | 3 +- .../Documents/LeaseDataDocument.cs | 2 +- .../Documents/MessageDataDocumentBase.cs | 8 +- .../Queries/DocumentQueryExtensions.cs | 13 +- .../Documents/Serializers/IdSerializer.cs | 2 +- .../Serializers/TimeStampSerializer.cs | 2 +- .../Serializers/VersionSerializer.cs | 16 +-- .../Documents/SourceDataDocumentBase.cs | 5 +- .../Documents/StateDocument.cs | 8 +- .../Documents/TagDataDocument.cs | 2 +- .../Sources/MongoDbSourceRepository.cs | 28 ++-- .../FilterBuilders/DataFilterBuilderBase.cs | 5 +- .../MessageDataFilterBuilder.cs | 10 +- .../FilterBuilders/SourceDataFilterBuilder.cs | 4 +- .../States/MongoDbStateRepository.cs | 9 +- .../States/Sessions/IMongoSession.cs | 6 +- .../States/Sessions/MongoSession.cs | 8 +- .../States/Sessions/TestModeMongoSession.cs | 6 +- .../MongoDbAtlas/DigestChallengeRequest.cs | 3 +- .../States/RedisStateRepository.cs | 7 +- .../States/Sessions/IRedisSession.cs | 8 +- .../States/Sessions/RedisSession.cs | 10 +- .../Entities/EntityRepositoryTests.cs | 14 +- .../Entities/EntityTests.cs | 23 ++-- .../Entities/Deltas/AddLease.cs | 2 +- .../Implementations/Entities/Deltas/AddTag.cs | 2 +- .../Entities/Deltas/DeleteLease.cs | 2 +- .../Entities/Deltas/DeleteTag.cs | 2 +- .../Entities/Deltas/DoNothing.cs | 4 +- .../Entities/Deltas/DoNothingIdempotent.cs | 12 ++ .../Entities/Deltas/StoreNumber.cs | 8 +- .../Implementations/Entities/TestEntity.cs | 17 ++- .../Projections/OneToOneProjection.cs | 22 ++-- .../Seeders/EntityRepositoryExtensions.cs | 2 +- .../Implementations/Seeders/LeaseSeeder.cs | 4 +- .../Implementations/Seeders/TagSeeder.cs | 4 +- .../Attributes/CountLease.cs | 4 +- .../Attributes/CountTag.cs | 4 +- .../Sources/Queries/CountDataQuery.cs | 2 +- .../Sources/Queries/SourceIdDataQuery.cs | 7 +- .../Queries/SourceTimeStampDataQuery.cs | 4 +- .../Sources/Queries/StateDataQuery.cs | 4 +- .../Sources/Queries/StateVersionDataQuery.cs | 5 +- .../Projections/ProjectionsTests.cs | 10 +- .../EntityStateSourceSubscriberTests.cs | 4 +- .../Sources/SourceTests.cs | 28 ++-- .../Sources/TryCatchSourceRepositoryTests.cs | 6 +- .../States/StateTests.cs | 15 +-- .../States/TryCatchStateRepositoryTests.cs | 7 +- .../Streams/StreamTests.cs | 95 +++++++------- test/EntityDb.Common.Tests/TestsBase.cs | 10 +- .../Seeder/HttpContextSeeder.cs | 2 +- 136 files changed, 805 insertions(+), 737 deletions(-) create mode 100644 src/EntityDb.Abstractions/Extensions/EnumerableExtensions.cs create mode 100644 src/EntityDb.Abstractions/Id.cs rename src/EntityDb.Abstractions/{States => Sources}/Attributes/ILease.cs (92%) create mode 100644 src/EntityDb.Abstractions/Sources/Attributes/IMessageKey.cs create mode 100644 src/EntityDb.Abstractions/Sources/Attributes/IStateKey.cs rename src/EntityDb.Abstractions/{States => Sources}/Attributes/ITag.cs (82%) rename src/EntityDb.Abstractions/{ValueObjects => Sources}/TimeStamp.cs (95%) create mode 100644 src/EntityDb.Abstractions/States/Deltas/IAddAlternateStateKeysDelta.cs create mode 100644 src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs create mode 100644 src/EntityDb.Abstractions/States/StatePointer.cs create mode 100644 src/EntityDb.Abstractions/States/StateVersion.cs delete mode 100644 src/EntityDb.Abstractions/ValueObjects/Id.cs delete mode 100644 src/EntityDb.Abstractions/ValueObjects/Key.cs delete mode 100644 src/EntityDb.Abstractions/ValueObjects/Pointer.cs delete mode 100644 src/EntityDb.Abstractions/ValueObjects/Version.cs create mode 100644 src/EntityDb.Common/Extensions/EnumerableExtensions.cs create mode 100644 src/EntityDb.Common/Sources/Attributes/Key.cs create mode 100644 src/EntityDb.Common/Sources/Attributes/Lease.cs create mode 100644 src/EntityDb.Common/Sources/Attributes/Tag.cs delete mode 100644 src/EntityDb.Common/States/Attributes/Lease.cs delete mode 100644 src/EntityDb.Common/States/Attributes/Tag.cs rename src/EntityDb.Common/Streams/{SingleEntityRepository.cs => SingleStreamRepository.cs} (71%) create mode 100644 test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothingIdempotent.cs rename test/EntityDb.Common.Tests/Implementations/{States => Sources}/Attributes/CountLease.cs (57%) rename test/EntityDb.Common.Tests/Implementations/{States => Sources}/Attributes/CountTag.cs (52%) diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs index c642cfea..34477f4b 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.Entities; @@ -32,7 +32,7 @@ Task> CreateSingleForNew /// Create a new instance of /// for an existing entity. /// - /// A pointer associated with a . + /// A state pointer associated with a . /// The name of the agent signature options. /// The agent's use case for the source repository. /// The agent's use case for the state repository. @@ -40,7 +40,7 @@ Task> CreateSingleForNew /// A new instance of . Task> CreateSingleForExisting ( - Pointer entityPointer, + StatePointer entityPointer, string agentSignatureOptionsName, string sourceSessionOptionsName, string? stateSessionOptionsName = null, diff --git a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs index 87b4cd03..9b4cdd29 100644 --- a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Entities; @@ -33,14 +32,14 @@ public interface IMultipleEntityRepository : IDisposableResource /// /// Associate a with a given state id. /// - /// A pointer associated with a . + /// A state pointer associated with a . /// A cancellation token /// A task. /// /// Call this method to load an entity that already exists before calling /// . /// - Task Load(Pointer entityPointer, CancellationToken cancellationToken = default); + Task Load(StatePointer entityPointer, CancellationToken cancellationToken = default); /// /// Returns the state of a for a given . diff --git a/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs index b3efef1f..5bcf362d 100644 --- a/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Entities; @@ -22,9 +21,9 @@ public interface ISingleEntityRepository : IDisposableResource IStateRepository? StateRepository { get; } /// - /// The pointer for the current entity. + /// The state pointer for the current entity. /// - Pointer EntityPointer { get; } + StatePointer EntityPointer { get; } /// /// Returns the state of a . diff --git a/src/EntityDb.Abstractions/Extensions/EnumerableExtensions.cs b/src/EntityDb.Abstractions/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..2ad55d04 --- /dev/null +++ b/src/EntityDb.Abstractions/Extensions/EnumerableExtensions.cs @@ -0,0 +1,14 @@ +namespace EntityDb.Abstractions.Extensions; + +internal static class EnumerableExtensions +{ + public static IEnumerable ConcatOrCoalesce(this IEnumerable? first, IEnumerable second) + { + return first == default ? second : first.Concat(second); + } + + public static IEnumerable AppendOrStart(this IEnumerable? source, T element) + { + return source == default ? new[] { element } : source.Append(element); + } +} diff --git a/src/EntityDb.Abstractions/Id.cs b/src/EntityDb.Abstractions/Id.cs new file mode 100644 index 00000000..f2b9683f --- /dev/null +++ b/src/EntityDb.Abstractions/Id.cs @@ -0,0 +1,45 @@ +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions; + +/// +/// Represents an identifier for a sources, messages, and states. +/// +/// The backing value. +public readonly record struct Id(Guid Value) +{ + /// + /// Returns a new, randomly-generated . + /// + /// A new, randomly-generated . + public static Id NewId() + { + return new Id(Guid.NewGuid()); + } + + /// + public override string ToString() + { + return Value.ToString(); + } + + /// + /// Implicitly converts a state id into a state pointer. + /// + /// The implicit state id argument. + public static implicit operator StatePointer(Id stateId) + { + return stateId + StateVersion.Zero; + } + + /// + /// Combine a state id and a state version into a state pointer. + /// + /// The state id. + /// The state version + /// A state pointer. + public static StatePointer operator +(Id stateId, StateVersion stateVersion) + { + return new StatePointer(stateId, stateVersion); + } +} diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index c3f97086..96b1683c 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Projections; @@ -21,13 +20,13 @@ public interface IProjection : IState /// Returns a that finds sources that need to be passed to the reducer. /// /// A service provider for fetching repositories. - /// A pointer to the desired projection state + /// The state pointer for the desired projection. /// A cancellation token /// - /// A that is used to load the rest of the messages for the given projection - /// pointer. + /// A that is used to load the rest of the messages + /// for the given state pointer. /// - IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Pointer projectionPointer, + IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, StatePointer projectionPointer, CancellationToken cancellationToken); /// diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index c9e20495..f0ae7298 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Projections; @@ -16,10 +15,10 @@ public interface IProjectionRepository : IDisposableResource IStateRepository? StateRepository { get; } /// - /// Returns the state of a for a given . + /// Returns the state of a for a given . /// - /// A pointer to the projection. + /// The state pointer to the projection. /// A cancellation token. /// The state of a for . - Task Get(Pointer projectionPointer, CancellationToken cancellationToken = default); + Task Get(StatePointer projectionPointer, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs b/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs index 40992be1..a7316136 100644 --- a/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs +++ b/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Sources.Agents; +namespace EntityDb.Abstractions.Sources.Agents; /// /// Represents an actor who can record sources. diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs index 742abcc3..dce12cfc 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.Sources.Annotations; @@ -24,12 +24,12 @@ public interface IAnnotatedMessageData Id MessageId { get; } /// - /// The data + /// The state pointer /// - TData Data { get; } + StatePointer StatePointer { get; } /// - /// A pointer to the state + /// The data /// - Pointer StatePointer { get; } + TData Data { get; } } diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs index 42fd4657..9dac7bbd 100644 --- a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.Sources.Annotations; @@ -19,17 +19,17 @@ public interface IAnnotatedSourceData TimeStamp SourceTimeStamp { get; } /// - /// The ids of the messages + /// The message ids /// Id[] MessageIds { get; } /// - /// The data + /// The state pointers /// - TData Data { get; } + StatePointer[] StatePointers { get; } /// - /// The pointers of the entities + /// The data /// - Pointer[] StatePointers { get; } + TData Data { get; } } diff --git a/src/EntityDb.Abstractions/States/Attributes/ILease.cs b/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs similarity index 92% rename from src/EntityDb.Abstractions/States/Attributes/ILease.cs rename to src/EntityDb.Abstractions/Sources/Attributes/ILease.cs index f3380a31..dca52959 100644 --- a/src/EntityDb.Abstractions/States/Attributes/ILease.cs +++ b/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.States.Attributes; +namespace EntityDb.Abstractions.Sources.Attributes; /// /// Represents a single metadata property and the context in which the metadata property must be unique. diff --git a/src/EntityDb.Abstractions/Sources/Attributes/IMessageKey.cs b/src/EntityDb.Abstractions/Sources/Attributes/IMessageKey.cs new file mode 100644 index 00000000..d1017367 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Attributes/IMessageKey.cs @@ -0,0 +1,6 @@ +namespace EntityDb.Abstractions.Sources.Attributes; + +public interface IMessageKey +{ + ILease ToLease(IStateKey stateKey); +} diff --git a/src/EntityDb.Abstractions/Sources/Attributes/IStateKey.cs b/src/EntityDb.Abstractions/Sources/Attributes/IStateKey.cs new file mode 100644 index 00000000..f5f6ccf4 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Attributes/IStateKey.cs @@ -0,0 +1,6 @@ +namespace EntityDb.Abstractions.Sources.Attributes; + +public interface IStateKey +{ + ILease ToLease(); +} diff --git a/src/EntityDb.Abstractions/States/Attributes/ITag.cs b/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs similarity index 82% rename from src/EntityDb.Abstractions/States/Attributes/ITag.cs rename to src/EntityDb.Abstractions/Sources/Attributes/ITag.cs index 859d1e10..22c11875 100644 --- a/src/EntityDb.Abstractions/States/Attributes/ITag.cs +++ b/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs @@ -1,4 +1,4 @@ -namespace EntityDb.Abstractions.States.Attributes; +namespace EntityDb.Abstractions.Sources.Attributes; /// /// Represents a single metadata property. diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs index b63c1555..f5f1f8fa 100644 --- a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -1,8 +1,8 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.Sources; @@ -48,12 +48,12 @@ IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default); /// - /// Returns the state pointers which are found by a agentSignature query. + /// Returns the state pointers which are found by a source data query. /// /// The source data query. /// A cancellation token. /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default); /// @@ -62,7 +62,7 @@ IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuer /// The message data query. /// A cancellation token. /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default); /// @@ -71,7 +71,7 @@ IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQu /// The lease query. /// A cancellation token. /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, + IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default); /// @@ -80,7 +80,7 @@ IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, /// The tag query. /// A cancellation token. /// The state pointers which are found by . - IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, + IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default); /// diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index 9cb79258..60b195f1 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -1,6 +1,7 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Extensions; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.States.Deltas; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.Sources; @@ -15,9 +16,9 @@ public sealed record Message public required Id Id { get; init; } /// - /// A pointer to the state + /// The state pointer /// - public required Pointer StatePointer { get; init; } + public required StatePointer StatePointer { get; init; } /// /// The data. @@ -27,41 +28,67 @@ public sealed record Message /// /// The leases to be added. /// - public IReadOnlyCollection AddLeases { get; init; } = Array.Empty(); + public ILease[] AddLeases { get; init; } = Array.Empty(); /// /// The tags to be added. /// - public IReadOnlyCollection AddTags { get; init; } = Array.Empty(); + public ITag[] AddTags { get; init; } = Array.Empty(); /// /// The leases to be deleted. /// - public IReadOnlyCollection DeleteLeases { get; init; } = Array.Empty(); + public ILease[] DeleteLeases { get; init; } = Array.Empty(); /// /// The tags to be deleted. /// - public IReadOnlyCollection DeleteTags { get; init; } = Array.Empty(); + public ITag[] DeleteTags { get; init; } = Array.Empty(); internal static Message NewMessage ( TState state, - Pointer statePointer, + StatePointer statePointer, object delta, - IReadOnlyCollection? additionalAddLeases = null + IStateKey? stateKey = default ) { - additionalAddLeases ??= Array.Empty(); - + IEnumerable? addLeases = default; + + if (stateKey != default) + { + if (statePointer.StateVersion == StateVersion.One) + { + addLeases = new[] { stateKey.ToLease() }; + } + + if (delta is IAddAlternateStateKeysDelta addAlternateStateKeysDelta) + { + addLeases = addLeases + .ConcatOrCoalesce(addAlternateStateKeysDelta + .GetAlternateStateKeys(state) + .Select(key => key.ToLease())); + } + + if (delta is IAddMessageKeyDelta addMessageKeyDelta) + { + addLeases = addLeases + .AppendOrStart(addMessageKeyDelta + .GetMessageKey() + .ToLease(stateKey)); + } + } + else if (delta is IAddLeasesDelta addLeasesDelta) + { + addLeases = addLeasesDelta.GetLeases(state); + } + return new Message { Id = Id.NewId(), StatePointer = statePointer, Delta = delta, - AddLeases = delta is IAddLeasesDelta addLeasesDelta - ? addLeasesDelta.GetLeases(state).Concat(additionalAddLeases).ToArray() - : additionalAddLeases, + AddLeases = addLeases?.ToArray() ?? Array.Empty(), AddTags = delta is IAddTagsDelta addTagsDelta ? addTagsDelta.GetTags(state).ToArray() : Array.Empty(), diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs index 8150fe70..aad50b61 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// /// Builds a for an object. Possible objects include: diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs index e872f502..1b8dbe08 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs index 03cd48d0..9a2de5fd 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs @@ -1,5 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; -using Version = EntityDb.Abstractions.ValueObjects.Version; +using EntityDb.Abstractions.States; namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; @@ -26,21 +25,21 @@ public interface IMessageDataFilterBuilder : IDataFilterBuilder that only includes objects with an state /// version greater than or equal to a specific state version. /// - /// The state ids. + /// The state ids. /// /// Returns a that only includes objects with an state - /// version greater than or equal to . + /// version greater than or equal to . /// - TFilter StateVersionGte(Version stateVersion); + TFilter StateVersionGte(StateVersion stateStateVersion); /// /// Returns a that only includes objects with an state /// version greater than or equal to a specific state version. /// - /// The state ids. + /// The state ids. /// /// Returns a that only includes objects with an state - /// version less than or equal to . + /// version less than or equal to . /// - TFilter StateVersionLte(Version stateVersion); + TFilter StateVersionLte(StateVersion stateStateVersion); } diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs index 19293691..7b027715 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; /// /// Builds a for an object associated diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs index f334876b..f202b4e8 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs index 7f2bc14a..2e3c7c97 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs index d160cccc..2cef1b0c 100644 --- a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; diff --git a/src/EntityDb.Abstractions/Sources/Source.cs b/src/EntityDb.Abstractions/Sources/Source.cs index 9ccc2ef5..0ebfcabe 100644 --- a/src/EntityDb.Abstractions/Sources/Source.cs +++ b/src/EntityDb.Abstractions/Sources/Source.cs @@ -1,6 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Sources; +namespace EntityDb.Abstractions.Sources; /// /// Represents a set of messages that can be committed. diff --git a/src/EntityDb.Abstractions/ValueObjects/TimeStamp.cs b/src/EntityDb.Abstractions/Sources/TimeStamp.cs similarity index 95% rename from src/EntityDb.Abstractions/ValueObjects/TimeStamp.cs rename to src/EntityDb.Abstractions/Sources/TimeStamp.cs index 12cac34e..552cb4ea 100644 --- a/src/EntityDb.Abstractions/ValueObjects/TimeStamp.cs +++ b/src/EntityDb.Abstractions/Sources/TimeStamp.cs @@ -1,6 +1,6 @@ using System.Globalization; -namespace EntityDb.Abstractions.ValueObjects; +namespace EntityDb.Abstractions.Sources; /// /// Represents a relevant moment in time. diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddAlternateStateKeysDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddAlternateStateKeysDelta.cs new file mode 100644 index 00000000..fd6c533e --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IAddAlternateStateKeysDelta.cs @@ -0,0 +1,8 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.States.Deltas; + +public interface IAddAlternateStateKeysDelta +{ + IEnumerable GetAlternateStateKeys(TState state); +} diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs index 04595bfa..7b76c214 100644 --- a/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs +++ b/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.States.Deltas; diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs new file mode 100644 index 00000000..947d6b40 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs @@ -0,0 +1,8 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.States.Deltas; + +public interface IAddMessageKeyDelta +{ + IMessageKey GetMessageKey(); +} diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs index 13ddf4b8..160fadd2 100644 --- a/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs +++ b/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.States.Deltas; diff --git a/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs index db305b66..842b2753 100644 --- a/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs +++ b/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.States.Deltas; diff --git a/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs index e7ba4981..5ac5b5a6 100644 --- a/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs +++ b/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.States.Deltas; diff --git a/src/EntityDb.Abstractions/States/IState.cs b/src/EntityDb.Abstractions/States/IState.cs index 3b5bbc28..a1823154 100644 --- a/src/EntityDb.Abstractions/States/IState.cs +++ b/src/EntityDb.Abstractions/States/IState.cs @@ -1,5 +1,3 @@ -using EntityDb.Abstractions.ValueObjects; - namespace EntityDb.Abstractions.States; /// @@ -11,15 +9,15 @@ public interface IState /// /// Creates a new instance of a . /// - /// The pointer of the state. + /// The state pointer. /// A new instance of . - static abstract TState Construct(Pointer pointer); + static abstract TState Construct(StatePointer statePointer); /// - /// Returns a pointer for the current state of the state. + /// Returns the state pointer for the current state. /// - /// A pointer for the current state of the state - Pointer GetPointer(); + /// The state pointer for the current state. + StatePointer GetPointer(); /// /// Indicates if this state instance version should be recorded (independent of the latest state). diff --git a/src/EntityDb.Abstractions/States/IStateRepository.cs b/src/EntityDb.Abstractions/States/IStateRepository.cs index ef886eda..86fa1ba5 100644 --- a/src/EntityDb.Abstractions/States/IStateRepository.cs +++ b/src/EntityDb.Abstractions/States/IStateRepository.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Abstractions.States; @@ -13,28 +12,28 @@ public interface IStateRepository : IDisposableResource /// Returns an exact version of state of a or /// default(). /// - /// A pointer to a specific state. + /// The state pointer. /// A cancellation token. /// /// An exact version of state of a or /// default(). /// - Task Get(Pointer statePointer, CancellationToken cancellationToken = default); + Task Get(StatePointer statePointer, CancellationToken cancellationToken = default); /// /// Inserts a state. /// - /// A pointer to a state. + /// The state pointer. /// The state. /// A cancellation token. /// true if the insert succeeded, or false if the insert failed. - Task Put(Pointer statePointer, TState state, CancellationToken cancellationToken = default); + Task Put(StatePointer statePointer, TState state, CancellationToken cancellationToken = default); /// /// Deletes multiple states. /// - /// Pointers to specific states. + /// The state pointers to delete. /// A cancellation token. /// true if the deletes all succeeded, or false if any deletes failed. - Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default); + Task Delete(StatePointer[] statePointers, CancellationToken cancellationToken = default); } diff --git a/src/EntityDb.Abstractions/States/StatePointer.cs b/src/EntityDb.Abstractions/States/StatePointer.cs new file mode 100644 index 00000000..2db85c66 --- /dev/null +++ b/src/EntityDb.Abstractions/States/StatePointer.cs @@ -0,0 +1,28 @@ +namespace EntityDb.Abstractions.States; + +/// +/// References a state with a given id at a given version +/// +/// The id of the state. +/// The version of the state. +public readonly record struct StatePointer(Id Id, StateVersion StateVersion) +{ + /// + /// Prints out {Id}@{Version}. + /// See and + /// + /// + public override string ToString() + { + return $"{Id}@{StateVersion}"; + } + + /// + /// Returns the next state pointer. + /// + /// The next state pointer. + public StatePointer Next() + { + return Id + StateVersion.Next(); + } +} diff --git a/src/EntityDb.Abstractions/States/StateVersion.cs b/src/EntityDb.Abstractions/States/StateVersion.cs new file mode 100644 index 00000000..cb652737 --- /dev/null +++ b/src/EntityDb.Abstractions/States/StateVersion.cs @@ -0,0 +1,35 @@ +namespace EntityDb.Abstractions.States; + +/// +/// Represents a version for an state. +/// +/// The backing value. +public readonly record struct StateVersion(ulong Value) +{ + /// + /// In the context of a this + /// refers to the latest state. In the context of a state + /// itself, this refers to the initial state. + /// + public static readonly StateVersion Zero = new(ulong.MinValue); + + /// + /// This always refers to the first version of a state. + /// + public static readonly StateVersion One = new(1); + + /// + /// Returns the next version. + /// + /// The next version. + public StateVersion Next() + { + return new StateVersion(Value + 1); + } + + /// + public override string ToString() + { + return Value.ToString(); + } +} diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs index 1e6da3e1..53a44811 100644 --- a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -1,6 +1,7 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; namespace EntityDb.Abstractions.Streams; @@ -14,29 +15,30 @@ public interface IMultipleStreamRepository : IDisposableResource /// ISourceRepository SourceRepository { get; } - void Create(Key streamKey); - - Task Load(Key streamKey, CancellationToken cancellationToken = default); - + void Create(IStateKey streamKey); + + Task Load(IStateKey streamKey, CancellationToken cancellationToken = default); + /// /// Load a stream if it exists, or create a new stream. /// /// A key associated with the stream. /// A cancellation token /// A task. - Task LoadOrCreate(Key streamKey, CancellationToken cancellationToken = default); + Task LoadOrCreate(IStateKey streamKey, CancellationToken cancellationToken = default); - void Append(Key streamKey, object delta); - /// - /// Stages a single delta with a given state key and message key. + /// Stages a single delta with a given state key. /// /// The key associated with the stream. - /// The key associated with the message. /// The new delta that modifies the stream. /// A cancellation token. - /// Only false if the message key already exists. - Task Append(Key streamKey, Key messageKey, object delta, CancellationToken cancellationToken = default); + /// + /// Only async if the delta implements . + /// Only false if the the delta implements and + /// the message key already exists. + /// + Task Append(IStateKey streamKey, object delta, CancellationToken cancellationToken = default); /// /// Commits all stages deltas. diff --git a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs index 7c23c36b..a137056a 100644 --- a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs @@ -1,6 +1,7 @@ using EntityDb.Abstractions.Disposables; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; namespace EntityDb.Abstractions.Streams; @@ -15,18 +16,21 @@ public interface ISingleStreamRepository : IDisposableResource ISourceRepository SourceRepository { get; } /// - /// The pointer for the current stream. + /// The state pointer for the current stream. /// - Key StreamKey { get; } + IStateKey StreamKey { get; } /// /// Stages a single delta with a given state key and message key. /// - /// The key associated with the message. /// The new delta that modifies the stream. /// A cancellation token. - /// Only false if the message key already exists. - Task Stage(Key messageKey, object delta, CancellationToken cancellationToken = default); + /// + /// Only async if the delta implements . + /// Only false if the the delta implements and + /// the message key already exists. + /// + Task Append(object delta, CancellationToken cancellationToken = default); /// /// Commits all stages deltas. diff --git a/src/EntityDb.Abstractions/Streams/IStream.cs b/src/EntityDb.Abstractions/Streams/IStream.cs index 36ddee44..5a6a4d35 100644 --- a/src/EntityDb.Abstractions/Streams/IStream.cs +++ b/src/EntityDb.Abstractions/Streams/IStream.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.Streams; public interface IStream { - Key Key { get; } + IStateKey Key { get; } Id Id { get; } } diff --git a/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs index 54a1e583..5236c0f2 100644 --- a/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs @@ -1,5 +1,5 @@ using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources.Attributes; namespace EntityDb.Abstractions.Streams; @@ -19,27 +19,27 @@ public interface IStreamRepositoryFactory /// A new instance of . Task CreateSingle ( - Key streamKey, + IStateKey streamKey, string agentSignatureOptionsName, string sourceSessionOptionsName, CancellationToken cancellationToken = default ); - + Task CreateSingleForNew ( - Key streamKey, + IStateKey streamKey, string agentSignatureOptionsName, string sourceSessionOptionsName, CancellationToken cancellationToken = default ); - + Task CreateSingleForExisting - ( - Key streamKey, - string agentSignatureOptionsName, - string sourceSessionOptionsName, - CancellationToken cancellationToken = default - ); + ( + IStateKey streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ); /// /// Creates a new instance of . diff --git a/src/EntityDb.Abstractions/ValueObjects/Id.cs b/src/EntityDb.Abstractions/ValueObjects/Id.cs deleted file mode 100644 index e221efd5..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/Id.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// Represents an identifier for a state. -/// -/// The backing value. -public readonly record struct Id(Guid Value) -{ - /// - /// Returns a new, randomly-generated . - /// - /// A new, randomly-generated . - public static Id NewId() - { - return new Id(Guid.NewGuid()); - } - - /// - public override string ToString() - { - return Value.ToString(); - } - - /// - /// Implicitly converts an to a pointer. - /// - /// The implicit id argument. - public static implicit operator Pointer(Id id) - { - return id + Version.Zero; - } - - /// - /// Combine an Id and a Version into a Pointer. - /// - /// - /// - /// A pointer for the id and version. - public static Pointer operator +(Id id, Version version) - { - return new Pointer(id, version); - } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/Key.cs b/src/EntityDb.Abstractions/ValueObjects/Key.cs deleted file mode 100644 index a9a26fdf..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/Key.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// Represents a key for a stream. -/// -/// The backing value. -public readonly record struct Key(string Value) -{ - /// - public override string ToString() - { - return Value; - } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs b/src/EntityDb.Abstractions/ValueObjects/Pointer.cs deleted file mode 100644 index 4b4619db..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// References a state with a given id at a given version -/// -/// The id of the state. -/// The version of the state. -public readonly record struct Pointer(Id Id, Version Version) -{ - /// - /// Prints out {Id}@{Version}. - /// See and - /// - /// - public override string ToString() - { - return $"{Id}@{Version}"; - } - - /// - /// Checks if the pointer found satisfies the pointer. - /// - /// The actual version found via queries. - /// true if - public bool IsSatisfiedBy(Pointer actualPointer) - { - if (Id != actualPointer.Id) - { - return false; - } - - if (Version == Version.Zero) - { - return actualPointer.Version != Version.Zero; - } - - return actualPointer.Version == Version; - } - - /// - /// Returns the next pointer. - /// - /// The next pointer. - public Pointer Next() - { - return Id + Version.Next(); - } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/Version.cs b/src/EntityDb.Abstractions/ValueObjects/Version.cs deleted file mode 100644 index a2369700..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/Version.cs +++ /dev/null @@ -1,38 +0,0 @@ -using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.States; - -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// Represents a version for an state. -/// -/// The backing value. -public readonly record struct Version(ulong Value) -{ - /// - /// This constant represents the minimum possible version. - /// In the context of an , - /// this value is reserved for auto-increment behavior. - /// In the context of an , - /// this value is reserved to point to the latest state. - /// - public static readonly Version Zero = new(ulong.MinValue); - - - public static readonly Version One = new(1); - - /// - /// Returns the next version. - /// - /// The next version. - public Version Next() - { - return new Version(Value + 1); - } - - /// - public override string ToString() - { - return Value.ToString(); - } -} diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index ac53db13..13dffdac 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -1,8 +1,8 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Entities; @@ -44,7 +44,7 @@ public async Task> CreateSingleForNew public async Task> CreateSingleForExisting ( - Pointer entityPointer, + StatePointer entityPointer, string agentSignatureOptionsName, string sourceSessionOptionsName, string? stateSessionOptionsName = null, diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index c2842e5b..faeb3caa 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -1,22 +1,20 @@ -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Abstractions.States.Deltas; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Queries.Standard; -using Microsoft.Extensions.DependencyInjection; namespace EntityDb.Common.Entities; -internal sealed class MultipleEntityRepository : DisposableResourceBaseClass, IMultipleEntityRepository +internal sealed class MultipleEntityRepository : DisposableResourceBaseClass, + IMultipleEntityRepository where TEntity : IEntity { - private readonly string _agentSignatureOptionsName; private readonly IAgentAccessor _agentAccessor; + private readonly string _agentSignatureOptionsName; private readonly Dictionary _knownEntities = new(); private readonly List _messages = new(); @@ -30,7 +28,7 @@ public MultipleEntityRepository { _agentSignatureOptionsName = agentSignatureOptionsName; _agentAccessor = agentAccessor; - + SourceRepository = sourceRepository; StateRepository = stateRepository; } @@ -50,7 +48,7 @@ public void Create(Id entityId) _knownEntities.Add(entityId, entity); } - public async Task Load(Pointer entityPointer, CancellationToken cancellationToken = default) + public async Task Load(StatePointer entityPointer, CancellationToken cancellationToken = default) { if (_knownEntities.ContainsKey(entityPointer.Id)) { @@ -64,7 +62,7 @@ public async Task Load(Pointer entityPointer, CancellationToken cancellationToke var statePointer = state.GetPointer(); - var query = new GetDeltasDataQuery(entityPointer, statePointer.Version); + var query = new GetDeltasDataQuery(entityPointer, statePointer.StateVersion); var entity = await SourceRepository .EnumerateDeltas(query, cancellationToken) @@ -75,10 +73,7 @@ public async Task Load(Pointer entityPointer, CancellationToken cancellationToke cancellationToken ); - if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) - { - throw new StateDoesNotExistException(); - } + StateDoesNotExistException.ThrowIfNotAcceptable(entityPointer, entity.GetPointer()); _knownEntities.Add(entityPointer.Id, entity); } @@ -115,7 +110,7 @@ public async Task Commit(CancellationToken cancellationToken = default) } var agent = await _agentAccessor.GetAgent(_agentSignatureOptionsName, cancellationToken); - + var source = new Source { Id = Id.NewId(), diff --git a/src/EntityDb.Common/Entities/SingleEntityRepository.cs b/src/EntityDb.Common/Entities/SingleEntityRepository.cs index d4cae195..eb3172dc 100644 --- a/src/EntityDb.Common/Entities/SingleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/SingleEntityRepository.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; namespace EntityDb.Common.Entities; @@ -11,7 +10,8 @@ internal sealed class SingleEntityRepository : DisposableResourceBaseCl { private readonly IMultipleEntityRepository _multipleEntityRepository; - public SingleEntityRepository(IMultipleEntityRepository multipleEntityRepository, Pointer entityPointer) + public SingleEntityRepository(IMultipleEntityRepository multipleEntityRepository, + StatePointer entityPointer) { _multipleEntityRepository = multipleEntityRepository; @@ -20,7 +20,7 @@ public SingleEntityRepository(IMultipleEntityRepository multipleEntityR public ISourceRepository SourceRepository => _multipleEntityRepository.SourceRepository; public IStateRepository? StateRepository => _multipleEntityRepository.StateRepository; - public Pointer EntityPointer { get; } + public StatePointer EntityPointer { get; } public TEntity Get() { diff --git a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs index ac9e9915..8fe13324 100644 --- a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs +++ b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs @@ -1,7 +1,6 @@ using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using Microsoft.Extensions.Logging; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Exceptions; @@ -10,9 +9,9 @@ namespace EntityDb.Common.Exceptions; /// to /// with any /// where the value of -/// in +/// in /// -/// is not equal to +/// is not equal to /// of the committed previous version. /// /// @@ -23,15 +22,15 @@ namespace EntityDb.Common.Exceptions; public sealed class OptimisticConcurrencyException : Exception { /// - /// Throws a new if - /// is not equal to . + /// Throws a new if + /// is not equal to . /// - /// The expected previous version. - /// The actual previous version. - public static void ThrowIfMismatch(Version expectedPreviousVersion, - Version actualPreviousVersion) + /// The expected previous version. + /// The actual previous version. + public static void ThrowIfMismatch(StateVersion expectedPreviousStateVersion, + StateVersion actualPreviousStateVersion) { - if (expectedPreviousVersion != actualPreviousVersion) + if (expectedPreviousStateVersion != actualPreviousStateVersion) { throw new OptimisticConcurrencyException(); } diff --git a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs index f6fff059..fce3bcda 100644 --- a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs +++ b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Exceptions; @@ -8,4 +9,28 @@ namespace EntityDb.Common.Exceptions; /// , or /// cannot find the requested state. /// -public sealed class StateDoesNotExistException : Exception; +public sealed class StateDoesNotExistException : Exception +{ + private StateDoesNotExistException(string message) : base(message) + { + } + + /// + /// Checks if the state pointer found satisfies the state pointer requested. + /// + /// The state pointer requested by the agent. + /// The state pointer found by a query. + public static void ThrowIfNotAcceptable(StatePointer requestedStatePointer, StatePointer foundStatePointer) + { + if (requestedStatePointer.StateVersion == StateVersion.Zero && + foundStatePointer.StateVersion == StateVersion.Zero) + { + throw new StateDoesNotExistException("Requested latest but found none"); + } + + if (requestedStatePointer != foundStatePointer) + { + throw new StateDoesNotExistException($"Requested {requestedStatePointer} but found {foundStatePointer}."); + } + } +} diff --git a/src/EntityDb.Common/Extensions/EnumerableExtensions.cs b/src/EntityDb.Common/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..4751c64e --- /dev/null +++ b/src/EntityDb.Common/Extensions/EnumerableExtensions.cs @@ -0,0 +1,18 @@ +namespace EntityDb.Common.Extensions; + +internal static class EnumerableExtensions +{ + public static IEnumerable ConcatOrCoalesce(this IEnumerable? first, IEnumerable second) + { + return first != default + ? first.Concat(second) + : second; + } + + public static IEnumerable AppendOrStart(this IEnumerable? source, T element) + { + return source != default + ? source.Append(element) + : new[] { element }; + } +} diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index 78aa9389..42f26602 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; @@ -25,7 +24,7 @@ public ProjectionRepository public IStateRepository? StateRepository { get; } - public async Task Get(Pointer projectionPointer, CancellationToken cancellationToken = default) + public async Task Get(StatePointer projectionPointer, CancellationToken cancellationToken = default) { var projection = StateRepository is not null ? await StateRepository.Get(projectionPointer, cancellationToken) ?? @@ -39,10 +38,7 @@ public async Task Get(Pointer projectionPointer, CancellationToken projection.Mutate(source); } - if (!projectionPointer.IsSatisfiedBy(projection.GetPointer())) - { - throw new StateDoesNotExistException(); - } + StateDoesNotExistException.ThrowIfNotAcceptable(projectionPointer, projection.GetPointer()); return projection; } diff --git a/src/EntityDb.Common/Sources/Agents/StandardAgent.cs b/src/EntityDb.Common/Sources/Agents/StandardAgent.cs index b5be9b49..9f5c6fa0 100644 --- a/src/EntityDb.Common/Sources/Agents/StandardAgent.cs +++ b/src/EntityDb.Common/Sources/Agents/StandardAgent.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Sources.Agents; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; namespace EntityDb.Common.Sources.Agents; diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs index d1c68220..344b96fc 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs @@ -1,5 +1,7 @@ -using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Sources.Annotations; @@ -9,7 +11,7 @@ internal sealed record AnnotatedMessageData TimeStamp SourceTimeStamp, Id MessageId, TData Data, - Pointer StatePointer + StatePointer StatePointer ) : IAnnotatedMessageData { public static IAnnotatedMessageData CreateFromBoxedData @@ -18,7 +20,7 @@ public static IAnnotatedMessageData CreateFromBoxedData TimeStamp sourceTimeStamp, Id messageId, object boxedData, - Pointer statePointer + StatePointer statePointer ) { var dataAnnotationType = typeof(AnnotatedMessageData<>).MakeGenericType(boxedData.GetType()); diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs index eeb91e7e..b0d334dd 100644 --- a/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs @@ -1,5 +1,7 @@ -using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Sources.Annotations; @@ -9,7 +11,7 @@ internal sealed record AnnotatedSourceData TimeStamp SourceTimeStamp, Id[] MessageIds, TData Data, - Pointer[] StatePointers + StatePointer[] StatePointers ) : IAnnotatedSourceData { public static IAnnotatedSourceData CreateFromBoxedData @@ -18,7 +20,7 @@ public static IAnnotatedSourceData CreateFromBoxedData TimeStamp sourceTimeStamp, Id[] messageIds, object boxedData, - Pointer[] statePointers + StatePointer[] statePointers ) { var dataAnnotationType = typeof(AnnotatedSourceData<>).MakeGenericType(boxedData.GetType()); diff --git a/src/EntityDb.Common/Sources/Attributes/Key.cs b/src/EntityDb.Common/Sources/Attributes/Key.cs new file mode 100644 index 00000000..40395262 --- /dev/null +++ b/src/EntityDb.Common/Sources/Attributes/Key.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Common.Sources.Attributes; + +/// +/// Represents a key for a state. +/// +/// The backing value. +public readonly record struct Key(string Value) : IStateKey, IMessageKey +{ + private const string KeysScope = "Keys"; + private const string StateLabel = "State"; + private const string MessageLabel = "Message"; + + ILease IMessageKey.ToLease(IStateKey streamKey) + { + var streamKeyLease = streamKey.ToLease(); + + return new Lease($"{KeysScope}/{streamKeyLease.Value}", MessageLabel, Value); + } + + ILease IStateKey.ToLease() + { + return new Lease(KeysScope, StateLabel, Value); + } + + /// + public override string ToString() + { + return Value; + } +} diff --git a/src/EntityDb.Common/Sources/Attributes/Lease.cs b/src/EntityDb.Common/Sources/Attributes/Lease.cs new file mode 100644 index 00000000..9af7b151 --- /dev/null +++ b/src/EntityDb.Common/Sources/Attributes/Lease.cs @@ -0,0 +1,6 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Common.Sources.Attributes; + +/// +public readonly record struct Lease(string Scope, string Label, string Value) : ILease; diff --git a/src/EntityDb.Common/Sources/Attributes/Tag.cs b/src/EntityDb.Common/Sources/Attributes/Tag.cs new file mode 100644 index 00000000..be37bc24 --- /dev/null +++ b/src/EntityDb.Common/Sources/Attributes/Tag.cs @@ -0,0 +1,6 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Common.Sources.Attributes; + +/// +public readonly record struct Tag(string Label, string Value) : ITag; diff --git a/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs index 077fff74..0db77e5b 100644 --- a/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs +++ b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs @@ -1,5 +1,6 @@ -using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.States; using EntityDb.Common.Envelopes; using EntityDb.Common.Sources.Annotations; using System.Runtime.CompilerServices; @@ -33,29 +34,29 @@ Func, IAsyncEnumerable> mapToIds return ids; } - public static IAsyncEnumerable EnumeratePointers + public static IAsyncEnumerable EnumeratePointers ( this IAsyncEnumerable documents, int? skip, int? limit, - Func, IAsyncEnumerable> mapToPointers + Func, IAsyncEnumerable> mapToStatePointers ) { - var pointers = mapToPointers + var statePointers = mapToStatePointers .Invoke(documents) .Distinct(); if (skip.HasValue) { - pointers = pointers.Skip(skip.Value); + statePointers = statePointers.Skip(skip.Value); } if (limit.HasValue) { - pointers = pointers.Take(limit.Value); + statePointers = statePointers.Take(limit.Value); } - return pointers; + return statePointers; } public static async IAsyncEnumerable> EnumerateAnnotatedSourceData : IDocument { Id MessageId { get; } - Pointer StatePointer { get; } + StatePointer StatePointer { get; } } diff --git a/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs b/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs index 48fd6811..0b52cc45 100644 --- a/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs +++ b/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs @@ -1,9 +1,10 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Sources.Documents; internal interface ISourceDataDocument : IDocument { Id[] MessageIds { get; } - Pointer[] StatePointers { get; } + StatePointer[] StatePointers { get; } } diff --git a/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs index ca75c64c..751ca8d4 100644 --- a/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs @@ -1,6 +1,7 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -52,7 +53,7 @@ public async Task Process(Source source, CancellationToken cancellationToken) } var latestEntities = new Dictionary(); - var saveEntities = new Dictionary(); + var saveEntities = new Dictionary(); foreach (var message in source.Messages) { diff --git a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs index aa564ddc..76adb79d 100644 --- a/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs @@ -23,13 +23,15 @@ public sealed record ModifiedQueryOptions public bool ReverseSort { get; init; } /// - /// If not null, then the new query will return this value for . Otherwise, the new + /// If not null, then the new query will return this value for . Otherwise, the + /// new /// query will return the same as the original query. /// public int? ReplaceSkip { get; init; } /// - /// If not null, then the new query will return this value for . Otherwise, the new + /// If not null, then the new query will return this value for . Otherwise, the + /// new /// query will return the same as the original query. /// public int? ReplaceTake { get; init; } diff --git a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs index 69a85e6b..c2a22081 100644 --- a/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs +++ b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs @@ -42,7 +42,8 @@ public static IMessageDataQuery Modify(this IMessageDataQuery messageDataQuery, } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// Returns a new, modified . The way in which it is modified depends on the parameters + /// of /// this extension method. /// /// The lease query. @@ -50,11 +51,15 @@ public static IMessageDataQuery Modify(this IMessageDataQuery messageDataQuery, /// A new, modified . public static ILeaseDataQuery Modify(this ILeaseDataQuery leaseDataQuery, ModifiedQueryOptions modifiedQueryOptions) { - return new ModifiedLeaseDataQuery { ModifiedQueryOptions = modifiedQueryOptions, LeaseDataQuery = leaseDataQuery }; + return new ModifiedLeaseDataQuery + { + ModifiedQueryOptions = modifiedQueryOptions, LeaseDataQuery = leaseDataQuery, + }; } /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of this + /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// this /// extension method. /// /// The tag query. diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs index e9386d65..3ab29f91 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Common.Sources.Queries.Standard; diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs index 6d775f7a..9f0e9737 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs @@ -1,8 +1,8 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Sources.Queries.Standard; diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs index eb4c144e..910df225 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs @@ -1,24 +1,23 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; -using Version = EntityDb.Abstractions.ValueObjects.Version; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetDeltasDataQuery(Pointer StatePointer, Version PersistedStateVersion, +internal sealed record GetDeltasDataQuery(StatePointer StatePointer, StateVersion PersistedStateStateVersion, object? Options = null) : IMessageDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { var filters = new List { - builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(PersistedStateVersion.Next()), + builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(PersistedStateStateVersion.Next()), }; - if (StatePointer.Version != Version.Zero) + if (StatePointer.StateVersion != StateVersion.Zero) { - filters.Add(builder.StateVersionLte(StatePointer.Version)); + filters.Add(builder.StateVersionLte(StatePointer.StateVersion)); } return builder.And(filters.ToArray()); diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs index 1aba17cc..f59146ed 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Sources.Queries.Standard; diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs index b51d253c..18405fae 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Sources.Queries.Standard; diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs index 4fc6d07c..0fb57dc0 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs @@ -1,7 +1,7 @@ +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Common.Sources.Queries.Standard; diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs index 693f91e9..3d2d6be7 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs @@ -1,7 +1,7 @@ +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.States.Attributes; namespace EntityDb.Common.Sources.Queries.Standard; diff --git a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs index b6301bb2..a6b052b0 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Sources.Queries.Standard; namespace EntityDb.Common.Sources; diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs index 348f5f8d..474dc0a6 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -1,8 +1,9 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; namespace EntityDb.Common.Sources; @@ -40,25 +41,25 @@ public IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseDataQuery, cancellationToken)); } - public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagDataQuery, cancellationToken)); diff --git a/src/EntityDb.Common/States/Attributes/Lease.cs b/src/EntityDb.Common/States/Attributes/Lease.cs deleted file mode 100644 index 7375cdc5..00000000 --- a/src/EntityDb.Common/States/Attributes/Lease.cs +++ /dev/null @@ -1,6 +0,0 @@ -using EntityDb.Abstractions.States.Attributes; - -namespace EntityDb.Common.States.Attributes; - -/// -public sealed record Lease(string Scope, string Label, string Value) : ILease; diff --git a/src/EntityDb.Common/States/Attributes/Tag.cs b/src/EntityDb.Common/States/Attributes/Tag.cs deleted file mode 100644 index 86cacd69..00000000 --- a/src/EntityDb.Common/States/Attributes/Tag.cs +++ /dev/null @@ -1,6 +0,0 @@ -using EntityDb.Abstractions.States.Attributes; - -namespace EntityDb.Common.States.Attributes; - -/// -public sealed record Tag(string Label, string Value) : ITag; diff --git a/src/EntityDb.Common/States/StateRepositoryWrapper.cs b/src/EntityDb.Common/States/StateRepositoryWrapper.cs index e33a37fb..e434c860 100644 --- a/src/EntityDb.Common/States/StateRepositoryWrapper.cs +++ b/src/EntityDb.Common/States/StateRepositoryWrapper.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; namespace EntityDb.Common.States; @@ -17,19 +16,19 @@ IStateRepository stateRepository _stateRepository = stateRepository; } - public virtual Task Put(Pointer statePointer, TState state, + public virtual Task Put(StatePointer statePointer, TState state, CancellationToken cancellationToken = default) { return WrapCommand(() => _stateRepository.Put(statePointer, state, cancellationToken)); } - public virtual Task Get(Pointer statePointer, + public virtual Task Get(StatePointer statePointer, CancellationToken cancellationToken = default) { return WrapQuery(() => _stateRepository.Get(statePointer, cancellationToken)); } - public virtual Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + public virtual Task Delete(StatePointer[] statePointers, CancellationToken cancellationToken = default) { return WrapCommand(() => _stateRepository.Delete(statePointers, cancellationToken)); } diff --git a/src/EntityDb.Common/States/TestModeStateManager.cs b/src/EntityDb.Common/States/TestModeStateManager.cs index 96f49a12..19b19a67 100644 --- a/src/EntityDb.Common/States/TestModeStateManager.cs +++ b/src/EntityDb.Common/States/TestModeStateManager.cs @@ -1,28 +1,27 @@ using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; namespace EntityDb.Common.States; internal sealed class TestModeStateManager : DisposableResourceBaseClass { - private readonly Dictionary, List> _dictionary = new(); + private readonly Dictionary, List> _dictionary = new(); - private List GetStoredStatePointers(IStateRepository stateRepository) + private List GetStoredStatePointers(IStateRepository stateRepository) { if (_dictionary.TryGetValue(stateRepository, out var storedStatePointers)) { return storedStatePointers; } - storedStatePointers = new List(); + storedStatePointers = new List(); _dictionary.Add(stateRepository, storedStatePointers); return storedStatePointers; } - public void AddStatePointer(IStateRepository stateRepository, Pointer statePointer) + public void AddStatePointer(IStateRepository stateRepository, StatePointer statePointer) { var storedStatePointers = GetStoredStatePointers(stateRepository); @@ -30,7 +29,7 @@ public void AddStatePointer(IStateRepository stateRepository, Pointer st } public void RemoveStatePointers(IStateRepository stateRepository, - IEnumerable statePointers) + IEnumerable statePointers) { var storedStatePointers = GetStoredStatePointers(stateRepository); diff --git a/src/EntityDb.Common/States/TestModeStateRepository.cs b/src/EntityDb.Common/States/TestModeStateRepository.cs index a80cf50b..6705a5f4 100644 --- a/src/EntityDb.Common/States/TestModeStateRepository.cs +++ b/src/EntityDb.Common/States/TestModeStateRepository.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; namespace EntityDb.Common.States; @@ -20,7 +19,7 @@ TestModeStateManager testModeStateManager _testModeStateManager = testModeStateManager; } - public Task Put(Pointer statePointer, TState state, + public Task Put(StatePointer statePointer, TState state, CancellationToken cancellationToken = default) { _testModeStateManager.AddStatePointer(this, statePointer); @@ -28,12 +27,12 @@ public Task Put(Pointer statePointer, TState state, return _stateRepository.Put(statePointer, state, cancellationToken); } - public Task Get(Pointer statePointer, CancellationToken cancellationToken = default) + public Task Get(StatePointer statePointer, CancellationToken cancellationToken = default) { return _stateRepository.Get(statePointer, cancellationToken); } - public Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + public Task Delete(StatePointer[] statePointers, CancellationToken cancellationToken = default) { _testModeStateManager.RemoveStatePointers(this, statePointers); diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 1be0f0f3..b713a2e0 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -1,63 +1,59 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.States.Deltas; using EntityDb.Abstractions.Streams; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Queries.Standard; -using EntityDb.Common.States.Attributes; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Streams; internal sealed class MultipleStreamRepository : DisposableResourceBaseClass, IMultipleStreamRepository { - private const string RootScope = "Stream"; - private const string StreamKeyLabel = "StreamKey"; - private const string MessageKeyLabel = "MessageKey"; - private readonly string _agentSignatureOptionsName; private readonly IAgentAccessor _agentAccessor; - private readonly Dictionary _knownStreams = new(); + private readonly string _agentSignatureOptionsName; + private readonly Dictionary _knownStreams = new(); private readonly List _messages = new(); - public MultipleStreamRepository(IAgentAccessor agentAccessor, string agentSignatureOptionsName, ISourceRepository sourceRepository) + public MultipleStreamRepository(IAgentAccessor agentAccessor, string agentSignatureOptionsName, + ISourceRepository sourceRepository) { _agentSignatureOptionsName = agentSignatureOptionsName; _agentAccessor = agentAccessor; - + SourceRepository = sourceRepository; } public ISourceRepository SourceRepository { get; } - public async Task LoadOrCreate(Key streamKey, CancellationToken cancellationToken = default) + public async Task LoadOrCreate(IStateKey streamKey, CancellationToken cancellationToken = default) { if (_knownStreams.ContainsKey(streamKey)) { throw new ExistingStreamException(); } - var streamKeyLease = GetStreamKeyLease(streamKey); + var streamKeyLease = streamKey.ToLease(); var streamPointer = await GetStreamPointer(streamKeyLease, cancellationToken); - var stream = streamPointer == default - ? new Stream { Key = streamKey, Id = Id.NewId(), New = true } - : new Stream { Key = streamKey, Id = streamPointer.Id, New = false }; + var isNew = streamPointer == default; - _knownStreams.Add(streamKey, stream); + _knownStreams.Add(streamKey, + new Stream { Key = streamKey, Id = isNew ? Id.NewId() : streamPointer.Id, IsNew = isNew }); } - public async Task Load(Key streamKey, CancellationToken cancellationToken = default) + public async Task Load(IStateKey streamKey, CancellationToken cancellationToken = default) { if (_knownStreams.ContainsKey(streamKey)) { throw new ExistingStreamException(); } - var streamKeyLease = GetStreamKeyLease(streamKey); + var streamKeyLease = streamKey.ToLease(); var streamPointer = await GetStreamPointer(streamKeyLease, cancellationToken); @@ -66,91 +62,54 @@ public async Task Load(Key streamKey, CancellationToken cancellationToken = defa throw new UnknownStreamKeyException(); } - _knownStreams.Add(streamKey, new Stream - { - Key = streamKey, - Id = streamPointer.Id, - New = false, - }); + _knownStreams.Add(streamKey, new Stream { Key = streamKey, Id = streamPointer.Id, IsNew = false }); } - public void Create(Key streamKey) + public void Create(IStateKey streamKey) { if (_knownStreams.ContainsKey(streamKey)) { throw new ExistingStreamException(); } - _knownStreams.Add(streamKey, new Stream - { - Key = streamKey, - Id = Id.NewId(), - New = true, - }); + _knownStreams.Add(streamKey, new Stream { Key = streamKey, Id = Id.NewId(), IsNew = true }); } - public void Append(Key streamKey, object delta) + public async Task Append(IStateKey streamKey, object delta, CancellationToken cancellationToken = default) { if (!_knownStreams.TryGetValue(streamKey, out var stream)) { throw new UnknownStreamKeyException(); } - var addLeases = new List(); - - Pointer nextStreamPointer; - - if (stream.New) + if (delta is IAddMessageKeyDelta addMessageKeyDelta) { - addLeases.Add(GetStreamKeyLease(stream.Key)); + var messageKeyLease = addMessageKeyDelta + .GetMessageKey() + .ToLease(streamKey); - _knownStreams[streamKey] = stream with { New = false }; + var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); - nextStreamPointer = stream.Id + Version.One; - } - else - { - nextStreamPointer = stream.Id; + if (streamPointer != default) + { + return false; + } } - _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, addLeases)); - } + StatePointer nextStreamPointer; - public async Task Append(Key streamKey, Key messageKey, object delta, - CancellationToken cancellationToken = default) - { - if (!_knownStreams.TryGetValue(streamKey, out var stream)) + if (stream.IsNew) { - throw new UnknownStreamKeyException(); - } - - var messageKeyLease = GetMessageKeyLease(streamKey, messageKey); + _knownStreams[streamKey].IsNew = false; - var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); - - if (streamPointer != default) - { - return false; - } - - var addLeases = new List { messageKeyLease }; - - Pointer nextStreamPointer; - - if (stream.New) - { - addLeases.Add(GetStreamKeyLease(stream.Key)); - - _knownStreams[streamKey] = stream with { New = false }; - - nextStreamPointer = stream.Id + Version.One; + nextStreamPointer = stream.Id + StateVersion.One; } else { nextStreamPointer = stream.Id; } - _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, addLeases)); + _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, streamKey)); return true; } @@ -163,7 +122,7 @@ public async Task Commit(CancellationToken cancellationToken = default) } var agent = await _agentAccessor.GetAgent(_agentSignatureOptionsName, cancellationToken); - + var source = new Source { Id = Id.NewId(), @@ -189,17 +148,7 @@ public override ValueTask DisposeAsync() return SourceRepository.DisposeAsync(); } - public static ILease GetStreamKeyLease(Key streamKey) - { - return new Lease(RootScope, StreamKeyLabel, streamKey.Value); - } - - public static ILease GetMessageKeyLease(Key streamKey, Key messageKey) - { - return new Lease($"{RootScope}/{streamKey}", MessageKeyLabel, messageKey.Value); - } - - private async Task GetStreamPointer(ILease lease, CancellationToken cancellationToken) + private async Task GetStreamPointer(ILease lease, CancellationToken cancellationToken) { var query = new MatchingLeaseDataQuery(lease); diff --git a/src/EntityDb.Common/Streams/SingleEntityRepository.cs b/src/EntityDb.Common/Streams/SingleStreamRepository.cs similarity index 71% rename from src/EntityDb.Common/Streams/SingleEntityRepository.cs rename to src/EntityDb.Common/Streams/SingleStreamRepository.cs index 3f2e473b..59be1318 100644 --- a/src/EntityDb.Common/Streams/SingleEntityRepository.cs +++ b/src/EntityDb.Common/Streams/SingleStreamRepository.cs @@ -1,6 +1,6 @@ using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Streams; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; namespace EntityDb.Common.Streams; @@ -9,7 +9,7 @@ internal sealed class SingleStreamRepository : DisposableResourceBaseClass, ISin { private readonly IMultipleStreamRepository _multipleStreamRepository; - public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository, Key streamKey) + public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository, IStateKey streamKey) { _multipleStreamRepository = multipleStreamRepository; @@ -17,11 +17,11 @@ public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository } public ISourceRepository SourceRepository => _multipleStreamRepository.SourceRepository; - public Key StreamKey { get; } + public IStateKey StreamKey { get; } - public Task Stage(Key messageKey, object delta, CancellationToken cancellationToken = default) + public Task Append(object delta, CancellationToken cancellationToken = default) { - return _multipleStreamRepository.Append(StreamKey, messageKey, delta, cancellationToken); + return _multipleStreamRepository.Append(StreamKey, delta, cancellationToken); } public Task Commit(CancellationToken cancellationToken = default) diff --git a/src/EntityDb.Common/Streams/Stream.cs b/src/EntityDb.Common/Streams/Stream.cs index 8fd072aa..7b42b9a2 100644 --- a/src/EntityDb.Common/Streams/Stream.cs +++ b/src/EntityDb.Common/Streams/Stream.cs @@ -1,11 +1,12 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Streams; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Streams; -internal sealed record Stream : IStream +internal sealed class Stream : IStream { - public required Key Key { get; init; } + public required bool IsNew { get; set; } + public required IStateKey Key { get; init; } public required Id Id { get; init; } - public required bool New { get; init; } } diff --git a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs index 7db804f7..a1321bb3 100644 --- a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs +++ b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Streams; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Streams; @@ -22,7 +22,7 @@ ISourceRepositoryFactory sourceRepositoryFactory public async Task CreateSingle ( - Key streamKey, + IStateKey streamKey, string agentSignatureOptionsName, string sourceSessionOptionsName, CancellationToken cancellationToken = default @@ -38,7 +38,7 @@ public async Task CreateSingle public async Task CreateSingleForNew ( - Key streamKey, + IStateKey streamKey, string agentSignatureOptionsName, string sourceSessionOptionsName, CancellationToken cancellationToken = default @@ -54,7 +54,7 @@ public async Task CreateSingleForNew public async Task CreateSingleForExisting ( - Key streamKey, + IStateKey streamKey, string agentSignatureOptionsName, string sourceSessionOptionsName, CancellationToken cancellationToken = default diff --git a/src/EntityDb.Json/Converters/IdConverter.cs b/src/EntityDb.Json/Converters/IdConverter.cs index d28a697c..1b16fa8a 100644 --- a/src/EntityDb.Json/Converters/IdConverter.cs +++ b/src/EntityDb.Json/Converters/IdConverter.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/src/EntityDb.Json/Converters/VersionConverter.cs b/src/EntityDb.Json/Converters/VersionConverter.cs index b73b9bb5..22e7a87f 100644 --- a/src/EntityDb.Json/Converters/VersionConverter.cs +++ b/src/EntityDb.Json/Converters/VersionConverter.cs @@ -1,12 +1,12 @@ +using EntityDb.Abstractions.States; using System.Text.Json; using System.Text.Json.Serialization; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Json.Converters; -internal sealed class VersionConverter : JsonConverter +internal sealed class VersionConverter : JsonConverter { - public override Version Read + public override StateVersion Read ( ref Utf8JsonReader reader, Type typeToConvert, @@ -20,17 +20,17 @@ JsonSerializerOptions options var ulongValue = reader.GetUInt64(); - return new Version(ulongValue); + return new StateVersion(ulongValue); } public override void Write ( Utf8JsonWriter writer, - Version version, + StateVersion stateVersion, JsonSerializerOptions options ) { - var ulongValue = version.Value; + var ulongValue = stateVersion.Value; writer.WriteNumberValue(ulongValue); } diff --git a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs index 237617fe..b6dbf3ca 100644 --- a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs @@ -1,6 +1,7 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Envelopes; using EntityDb.Common.Sources.Queries.Standard; using EntityDb.MongoDb.Documents.Commands; @@ -11,7 +12,6 @@ using EntityDb.MongoDb.Sources.Sessions; using MongoDB.Bson; using MongoDB.Driver; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Documents; @@ -45,7 +45,7 @@ Message message SourceId = source.Id, MessageId = message.Id, StateId = message.StatePointer.Id, - StateVersion = message.StatePointer.Version, + StateVersion = message.StatePointer.StateVersion, StatePointer = message.StatePointer, DataType = message.Delta.GetType().Name, Data = envelopeService.Serialize(message.Delta), @@ -75,7 +75,7 @@ IMessageDataQuery messageDataQuery }; } - public static async Task GetLastStateVersion + public static async Task GetLastStateVersion ( IMongoSession mongoSession, Id stateId, diff --git a/src/EntityDb.MongoDb/Documents/DocumentBase.cs b/src/EntityDb.MongoDb/Documents/DocumentBase.cs index ceb7c91d..5b0909be 100644 --- a/src/EntityDb.MongoDb/Documents/DocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/DocumentBase.cs @@ -1,4 +1,5 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; using EntityDb.Common.Sources.Documents; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; diff --git a/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs index f7de9080..15baba20 100644 --- a/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs @@ -37,7 +37,7 @@ Message message SourceId = source.Id, MessageId = message.Id, StateId = message.StatePointer.Id, - StateVersion = message.StatePointer.Version, + StateVersion = message.StatePointer.StateVersion, StatePointer = message.StatePointer, DataType = lease.GetType().Name, Data = envelopeService.Serialize(lease), diff --git a/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs index ddb93158..a2ef7abe 100644 --- a/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs @@ -1,8 +1,8 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; using EntityDb.Common.Sources.Documents; using MongoDB.Bson; using MongoDB.Driver; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Documents; @@ -12,8 +12,8 @@ internal abstract record MessageDataDocumentBase : DocumentBase, IMessageDataDoc ProjectionBuilder.Include(nameof(StatePointer)); public required Id StateId { get; init; } - public required Version StateVersion { get; init; } + public required StateVersion StateVersion { get; init; } public required Id MessageId { get; init; } - public required Pointer StatePointer { get; init; } + public required StatePointer StatePointer { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs index e54fc3eb..b4b1998c 100644 --- a/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs @@ -1,5 +1,6 @@ -using EntityDb.Abstractions.Sources.Annotations; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.States; using EntityDb.Common.Envelopes; using EntityDb.Common.Polyfills; using EntityDb.Common.Sources.Documents; @@ -31,12 +32,12 @@ CancellationToken cancellationToken return documents.EnumerateIds(skip, limit, mapToIds); } - private static IAsyncEnumerable EnumeratePointers + private static IAsyncEnumerable EnumeratePointers ( this DocumentQuery documentQuery, IMongoSession mongoSession, ProjectionDefinition projection, - Func, IAsyncEnumerable> mapToPointers, + Func, IAsyncEnumerable> mapToPointers, CancellationToken cancellationToken ) { @@ -67,7 +68,7 @@ CancellationToken cancellationToken ); } - public static IAsyncEnumerable EnumerateSourceDataStatePointers + public static IAsyncEnumerable EnumerateSourceDataStatePointers ( this DocumentQuery documentQuery, IMongoSession mongoSession, @@ -84,7 +85,7 @@ CancellationToken cancellationToken ); } - public static IAsyncEnumerable EnumerateMessageStatePointers + public static IAsyncEnumerable EnumerateMessageStatePointers ( this DocumentQuery documentQuery, IMongoSession mongoSession, diff --git a/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs index 33b34794..1d693759 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; using MongoDB.Bson.Serialization; namespace EntityDb.MongoDb.Documents.Serializers; diff --git a/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs index 4e1b49ef..4c6efaaf 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; using MongoDB.Bson.Serialization; namespace EntityDb.MongoDb.Documents.Serializers; diff --git a/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs index b1f8f732..c6733d80 100644 --- a/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/VersionSerializer.cs @@ -1,29 +1,29 @@ +using EntityDb.Abstractions.States; using MongoDB.Bson.Serialization; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Documents.Serializers; -internal sealed class VersionSerializer : IBsonSerializer +internal sealed class VersionSerializer : IBsonSerializer { - public Type ValueType => typeof(Version); + public Type ValueType => typeof(StateVersion); object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { return Deserialize(context, args); } - public Version Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + public StateVersion Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var longValue = context.Reader.ReadInt64(); var ulongValue = Convert.ToUInt64(longValue); - return new Version(ulongValue); + return new StateVersion(ulongValue); } public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - if (value is not Version version) + if (value is not StateVersion version) { throw new NotSupportedException(); } @@ -31,9 +31,9 @@ public void Serialize(BsonSerializationContext context, BsonSerializationArgs ar Serialize(context, args, version); } - public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Version version) + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, StateVersion stateVersion) { - var longValue = Convert.ToInt64(version.Value); + var longValue = Convert.ToInt64(stateVersion.Value); context.Writer.WriteInt64(longValue); } diff --git a/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs index 1c0c5320..88692249 100644 --- a/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs @@ -1,4 +1,5 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; using EntityDb.Common.Sources.Documents; using EntityDb.MongoDb.Sources.Queries.FilterBuilders; using MongoDB.Bson; @@ -16,5 +17,5 @@ internal abstract record SourceDataDocumentBase : DocumentBase, ISourceDataDocum public required Id[] StateIds { get; init; } public required Id[] MessageIds { get; init; } - public required Pointer[] StatePointers { get; init; } + public required StatePointer[] StatePointers { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/StateDocument.cs b/src/EntityDb.MongoDb/Documents/StateDocument.cs index 8018b1bf..66ea162d 100644 --- a/src/EntityDb.MongoDb/Documents/StateDocument.cs +++ b/src/EntityDb.MongoDb/Documents/StateDocument.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Documents; @@ -13,6 +13,6 @@ internal sealed record StateDocument public required string DataType { get; init; } public required BsonDocument Data { get; init; } public required Id StateId { get; init; } - public required Version StateVersion { get; init; } - public required Pointer StatePointer { get; init; } + public required StateVersion StateVersion { get; init; } + public required StatePointer StatePointer { get; init; } } diff --git a/src/EntityDb.MongoDb/Documents/TagDataDocument.cs b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs index f89a65b3..4de8829f 100644 --- a/src/EntityDb.MongoDb/Documents/TagDataDocument.cs +++ b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs @@ -36,7 +36,7 @@ Message message SourceId = source.Id, MessageId = message.Id, StateId = message.StatePointer.Id, - StateVersion = message.StatePointer.Version, + StateVersion = message.StatePointer.StateVersion, StatePointer = message.StatePointer, DataType = tag.GetType().Name, Data = envelopeService.Serialize(tag), diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs index 116c7ca6..19db675c 100644 --- a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -1,8 +1,9 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; using EntityDb.Common.Exceptions; @@ -10,7 +11,6 @@ using EntityDb.MongoDb.Documents.Queries; using EntityDb.MongoDb.Sources.Sessions; using MongoDB.Bson; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Sources; @@ -61,7 +61,7 @@ public IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, .EnumerateSourceIds(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, CancellationToken cancellationToken = default) { return AgentSignatureDocument @@ -69,7 +69,7 @@ public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceD .EnumerateSourceDataStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, CancellationToken cancellationToken = default) { return DeltaDataDocument @@ -77,7 +77,7 @@ public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messag .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, CancellationToken cancellationToken = default) { return LeaseDataDocument @@ -85,7 +85,7 @@ public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDat .EnumerateMessageStatePointers(_mongoSession, cancellationToken); } - public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, + public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, CancellationToken cancellationToken = default) { return TagDataDocument @@ -162,7 +162,7 @@ public async Task Commit(Source source, var previousVersion = await DeltaDataDocument .GetLastStateVersion(_mongoSession, message.StatePointer.Id, cancellationToken); - if (message.StatePointer.Version == Version.Zero) + if (message.StatePointer.StateVersion == StateVersion.Zero) { currentMessage = currentMessage with { @@ -172,7 +172,7 @@ public async Task Commit(Source source, else { OptimisticConcurrencyException.ThrowIfMismatch(previousVersion.Next(), - message.StatePointer.Version); + message.StatePointer.StateVersion); } await Put(source, currentMessage, cancellationToken); @@ -210,28 +210,28 @@ await DeltaDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); - if (message.AddLeases.Count > 0) + if (message.AddLeases.Length > 0) { await LeaseDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); } - if (message.AddTags.Count > 0) + if (message.AddTags.Length > 0) { await TagDataDocument .GetInsertCommand(_envelopeService, source, message) .Execute(_mongoSession, cancellationToken); } - if (message.DeleteLeases.Count > 0) + if (message.DeleteLeases.Length > 0) { await LeaseDataDocument .GetDeleteCommand(message) .Execute(_mongoSession, cancellationToken); } - if (message.DeleteTags.Count > 0) + if (message.DeleteTags.Length > 0) { await TagDataDocument .GetDeleteCommand(message) diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs index 4a010e44..730d3847 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs @@ -1,5 +1,6 @@ -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Common.Envelopes; using EntityDb.MongoDb.Documents; using MongoDB.Bson; diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs index 819d10db..8dfabeb6 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.States; using EntityDb.MongoDb.Documents; using MongoDB.Bson; using MongoDB.Driver; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; @@ -20,12 +20,12 @@ public FilterDefinition StateIdIn(params Id[] stateIds) ); } - public FilterDefinition StateVersionGte(Version stateVersion) + public FilterDefinition StateVersionGte(StateVersion stateVersion) { return Gte(nameof(MessageDataDocumentBase.StateVersion), stateVersion); } - public FilterDefinition StateVersionLte(Version stateVersion) + public FilterDefinition StateVersionLte(StateVersion stateVersion) { return Lte(nameof(MessageDataDocumentBase.StateVersion), stateVersion); } diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs index 0be842e4..c1ea8698 100644 --- a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.Sources.Queries.FilterBuilders; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.MongoDb.Documents; using MongoDB.Bson; using MongoDB.Driver; diff --git a/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs index ef39b534..e6715f12 100644 --- a/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; using EntityDb.MongoDb.Documents; @@ -24,7 +23,7 @@ IMongoSession mongoSession _mongoSession = mongoSession; } - public async Task Put(Pointer statePointer, TState state, + public async Task Put(StatePointer statePointer, TState state, CancellationToken cancellationToken = default) { try @@ -36,7 +35,7 @@ await _mongoSession.Upsert(new StateDocument DataType = state.GetType().Name, Data = _envelopeService.Serialize(state), StateId = statePointer.Id, - StateVersion = statePointer.Version, + StateVersion = statePointer.StateVersion, StatePointer = statePointer, }, cancellationToken); @@ -54,7 +53,7 @@ await _mongoSession.Upsert(new StateDocument } } - public async Task Get(Pointer statePointer, + public async Task Get(StatePointer statePointer, CancellationToken cancellationToken = default) { var stateDocument = await _mongoSession.Fetch(statePointer, cancellationToken); @@ -68,7 +67,7 @@ await _mongoSession.Upsert(new StateDocument .Deserialize(stateDocument.Data); } - public async Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + public async Task Delete(StatePointer[] statePointers, CancellationToken cancellationToken = default) { await _mongoSession.Delete(statePointers, cancellationToken); diff --git a/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs index 0e356e45..748ad9c8 100644 --- a/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs +++ b/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs @@ -1,5 +1,5 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.MongoDb.Documents; using MongoDB.Driver; @@ -12,9 +12,9 @@ internal interface IMongoSession : IDisposableResource Task Upsert(StateDocument stateDocument, CancellationToken cancellationToken); - Task Fetch(Pointer statePointer, CancellationToken cancellationToken); + Task Fetch(StatePointer statePointer, CancellationToken cancellationToken); - Task Delete(Pointer[] statePointer, CancellationToken cancellationToken); + Task Delete(StatePointer[] statePointer, CancellationToken cancellationToken); void StartTransaction(); Task CommitTransaction(CancellationToken cancellationToken); diff --git a/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs index 48d524e4..eba20179 100644 --- a/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs +++ b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using EntityDb.MongoDb.Documents; @@ -62,7 +62,7 @@ await MongoDatabase public async Task Fetch ( - Pointer statePointer, + StatePointer statePointer, CancellationToken cancellationToken ) { @@ -103,7 +103,7 @@ CancellationToken cancellationToken return stateDocument; } - public async Task Delete(Pointer[] statePointer, CancellationToken cancellationToken) + public async Task Delete(StatePointer[] statePointer, CancellationToken cancellationToken) { AssertNotReadOnly(); @@ -209,7 +209,7 @@ public override ValueTask DisposeAsync() return base.DisposeAsync(); } - private static FilterDefinition GetFilter(Pointer statePointer) + private static FilterDefinition GetFilter(StatePointer statePointer) { return Filter.Eq(document => document.StatePointer, statePointer); } diff --git a/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs index 50885d68..42daf6fc 100644 --- a/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.MongoDb.Documents; using MongoDB.Driver; @@ -16,7 +16,7 @@ public Task Upsert(StateDocument stateDocument, CancellationToken cancellationTo return MongoSession.Upsert(stateDocument, cancellationToken); } - public Task Fetch(Pointer statePointer, CancellationToken cancellationToken) + public Task Fetch(StatePointer statePointer, CancellationToken cancellationToken) { return MongoSession.Fetch ( @@ -25,7 +25,7 @@ public Task Upsert(StateDocument stateDocument, CancellationToken cancellationTo ); } - public Task Delete(Pointer[] statePointer, CancellationToken cancellationToken) + public Task Delete(StatePointer[] statePointer, CancellationToken cancellationToken) { return MongoSession.Delete(statePointer, cancellationToken); } diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs b/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs index 59098a60..c1b112d6 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs @@ -6,7 +6,8 @@ namespace EntityDb.Provisioner.MongoDbAtlas; -internal sealed record DigestChallengeRequest(string? Realm, string? Domain, string? Nonce, string? Algorithm, string? Qop, +internal sealed record DigestChallengeRequest(string? Realm, string? Domain, string? Nonce, string? Algorithm, + string? Qop, string? Stale, uint NonceCount, string ClientNonce, DateTime ExpiresAt) { private const int MinClientNonce = 0x100000; diff --git a/src/EntityDb.Redis/States/RedisStateRepository.cs b/src/EntityDb.Redis/States/RedisStateRepository.cs index 96ca4c26..a3b8cbb1 100644 --- a/src/EntityDb.Redis/States/RedisStateRepository.cs +++ b/src/EntityDb.Redis/States/RedisStateRepository.cs @@ -1,5 +1,4 @@ using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Envelopes; using EntityDb.Redis.States.Sessions; @@ -21,7 +20,7 @@ IRedisSession redisSession _redisSession = redisSession; } - public async Task Put(Pointer statePointer, TState state, + public async Task Put(StatePointer statePointer, TState state, CancellationToken cancellationToken = default) { var stateValue = _envelopeService @@ -30,7 +29,7 @@ public async Task Put(Pointer statePointer, TState state, return await _redisSession.Upsert(statePointer, stateValue).WaitAsync(cancellationToken); } - public async Task Get(Pointer statePointer, + public async Task Get(StatePointer statePointer, CancellationToken cancellationToken = default) { var stateValue = await _redisSession.Fetch(statePointer).WaitAsync(cancellationToken); @@ -44,7 +43,7 @@ public async Task Put(Pointer statePointer, TState state, .Deserialize(stateValue!); } - public Task Delete(Pointer[] statePointers, CancellationToken cancellationToken = default) + public Task Delete(StatePointer[] statePointers, CancellationToken cancellationToken = default) { return _redisSession.Delete(statePointers).WaitAsync(cancellationToken); } diff --git a/src/EntityDb.Redis/States/Sessions/IRedisSession.cs b/src/EntityDb.Redis/States/Sessions/IRedisSession.cs index ede98cbe..a1b74175 100644 --- a/src/EntityDb.Redis/States/Sessions/IRedisSession.cs +++ b/src/EntityDb.Redis/States/Sessions/IRedisSession.cs @@ -1,12 +1,12 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using StackExchange.Redis; namespace EntityDb.Redis.States.Sessions; internal interface IRedisSession : IDisposableResource { - Task Upsert(Pointer statePointer, RedisValue redisValue); - Task Fetch(Pointer statePointer); - Task Delete(Pointer[] statePointers); + Task Upsert(StatePointer statePointer, RedisValue redisValue); + Task Fetch(StatePointer statePointer); + Task Delete(StatePointer[] statePointers); } diff --git a/src/EntityDb.Redis/States/Sessions/RedisSession.cs b/src/EntityDb.Redis/States/Sessions/RedisSession.cs index 426a5c2d..13576539 100644 --- a/src/EntityDb.Redis/States/Sessions/RedisSession.cs +++ b/src/EntityDb.Redis/States/Sessions/RedisSession.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; @@ -14,7 +14,7 @@ internal sealed record RedisSession RedisStateSessionOptions Options ) : DisposableResourceBaseRecord, IRedisSession { - public async Task Upsert(Pointer statePointer, RedisValue redisValue) + public async Task Upsert(StatePointer statePointer, RedisValue redisValue) { AssertNotReadOnly(); @@ -48,7 +48,7 @@ public async Task Upsert(Pointer statePointer, RedisValue redisValue) return upserted; } - public async Task Fetch(Pointer statePointer) + public async Task Fetch(StatePointer statePointer) { var redisKey = GetRedisKey(statePointer); @@ -74,7 +74,7 @@ public async Task Fetch(Pointer statePointer) return redisValue; } - public async Task Delete(Pointer[] statePointers) + public async Task Delete(StatePointer[] statePointers) { AssertNotReadOnly(); @@ -130,7 +130,7 @@ private void AssertNotReadOnly() } } - private RedisKey GetRedisKey(Pointer statePointer) + private RedisKey GetRedisKey(StatePointer statePointer) { return $"{Options.KeyNamespace}#{statePointer}"; } diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs index a9baeffd..60eae4bc 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -1,14 +1,14 @@ -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Exceptions; -using EntityDb.Common.States.Attributes; +using EntityDb.Common.Sources.Attributes; using EntityDb.Common.Tests.Implementations.Entities.Deltas; using Microsoft.Extensions.DependencyInjection; using Shouldly; using System.Diagnostics.CodeAnalysis; using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Entities; @@ -107,7 +107,7 @@ private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_Th committedSources.Count.ShouldBe(1); committedSources[0].Messages.Length.ShouldBe(1); - committedSources[0].Messages[0].AddLeases.Count.ShouldBe(1); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); } private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( @@ -180,13 +180,13 @@ private async Task Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVers committedSources.Count.ShouldBe(1); - var expectedVersion = Version.Zero; + var expectedVersion = StateVersion.Zero; for (var i = 1; i <= numberOfVersionsToTest; i++) { expectedVersion = expectedVersion.Next(); - committedSources[0].Messages[i - 1].StatePointer.Version.ShouldBe(expectedVersion); + committedSources[0].Messages[i - 1].StatePointer.StateVersion.ShouldBe(expectedVersion); } } diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index b580f9ae..3f2fd25a 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -1,8 +1,8 @@ -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Exceptions; using EntityDb.Common.Polyfills; using EntityDb.Common.Sources.Agents; @@ -14,7 +14,6 @@ using Shouldly; using System.Diagnostics.CodeAnalysis; using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Entities; @@ -45,7 +44,7 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe const ulong n = 10UL; const ulong m = 5UL; - var versionM = new Version(m); + var versionM = new StateVersion(m); var entityId = Id.NewId(); @@ -61,13 +60,13 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe // ACT - await readOnlyRepository.Load(entityId + new Version(m)); + await readOnlyRepository.Load(entityId + new StateVersion(m)); var entity = readOnlyRepository.Get(entityId); // ASSERT - entity.GetPointer().Version.ShouldBe(versionM); + entity.GetPointer().StateVersion.ShouldBe(versionM); } private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEntity_ThenGetDeltasRuns( @@ -77,7 +76,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn // ARRANGE const uint expectedVersionNumber = 10; - var expectedVersion = new Version(expectedVersionNumber); + var expectedVersion = new StateVersion(expectedVersionNumber); var entityId = Id.NewId(); @@ -139,7 +138,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn // ASSERT - entity.GetPointer().Version.ShouldBe(expectedVersion); + entity.GetPointer().StateVersion.ShouldBe(expectedVersion); sourceRepositoryMock .Verify( @@ -240,7 +239,7 @@ private async Task { // ARRANGE - var previousEntity = TEntity.Construct(default(Id) + new Version(1)); + var previousEntity = TEntity.Construct(default(Id) + new StateVersion(1)); var newDeltas = new object[] { new DoNothing(), new DoNothing() }; @@ -263,8 +262,8 @@ private async Task // ASSERT currentEntity.ShouldNotBe(previousEntity); - currentEntity.GetPointer().Version.ShouldBe( - new Version(previousEntity.GetPointer().Version.Value + Convert.ToUInt64(newDeltas.Length))); + currentEntity.GetPointer().StateVersion.ShouldBe( + new StateVersion(previousEntity.GetPointer().StateVersion.Value + Convert.ToUInt64(newDeltas.Length))); } private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow( @@ -301,7 +300,7 @@ private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEnti var entityId = Id.NewId(); - var expectedEntity = TEntity.Construct(entityId + new Version(1)); + var expectedEntity = TEntity.Construct(entityId + new StateVersion(1)); await using var entityRepository = await GetWriteEntityRepository(serviceScope, false); diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs index 6e9be7a9..1197b007 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.States.Deltas; namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs index b0c1e408..d16c388e 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.States.Deltas; namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs index db2e3e87..f441c992 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.States.Deltas; namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs index 551ec8b1..79967902 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs @@ -1,4 +1,4 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.States.Deltas; namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs index 84e2de6f..caf86358 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs @@ -7,11 +7,11 @@ public record DoNothing : IReducer, IMutator { public void Mutate(OneToOneProjection projection) { - projection.Pointer = projection.Pointer.Next(); + projection.StatePointer = projection.StatePointer.Next(); } public TestEntity Reduce(TestEntity entity) { - return new TestEntity { Pointer = entity.Pointer.Next() }; + return new TestEntity { StatePointer = entity.StatePointer.Next() }; } } diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothingIdempotent.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothingIdempotent.cs new file mode 100644 index 00000000..4f637387 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothingIdempotent.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public record DoNothingIdempotent(IMessageKey MessageKey) : DoNothing, IAddMessageKeyDelta +{ + public IMessageKey GetMessageKey() + { + return MessageKey; + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs index 7f206df3..19bb62fc 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs @@ -1,8 +1,8 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.States.Deltas; using EntityDb.Abstractions.States.Transforms; using EntityDb.Common.Tests.Implementations.Projections; -using EntityDb.Common.Tests.Implementations.States.Attributes; +using EntityDb.Common.Tests.Implementations.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; @@ -21,11 +21,11 @@ public IEnumerable GetTags(TestEntity state) public void Mutate(OneToOneProjection projection) { - projection.Pointer = projection.Pointer.Next(); + projection.StatePointer = projection.StatePointer.Next(); } public TestEntity Reduce(TestEntity entity) { - return new TestEntity { Pointer = entity.Pointer.Next() }; + return new TestEntity { StatePointer = entity.StatePointer.Next() }; } } diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 8d80234f..e999a87f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,23 +1,22 @@ using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.States.Transforms; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Tests.Implementations.States; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Entities; public sealed record TestEntity : IEntity, IStateWithTestLogic { - public required Pointer Pointer { get; init; } + public required StatePointer StatePointer { get; init; } - public static TestEntity Construct(Pointer pointer) + public static TestEntity Construct(StatePointer statePointer) { - return new TestEntity { Pointer = pointer }; + return new TestEntity { StatePointer = statePointer }; } - public Pointer GetPointer() + public StatePointer GetPointer() { - return Pointer; + return StatePointer; } public static bool CanReduce(object delta) @@ -53,8 +52,8 @@ public bool ShouldRecordAsLatest(TestEntity? previousLatestState) public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); - public TestEntity WithVersion(Version version) + public TestEntity WithVersion(StateVersion stateVersion) { - return new TestEntity { Pointer = Pointer.Id + version }; + return new TestEntity { StatePointer = StatePointer.Id + stateVersion }; } } diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index fd175958..0bcedae0 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,13 +1,13 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.States.Transforms; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Sources; using EntityDb.Common.Sources.Queries.Standard; using EntityDb.Common.Tests.Implementations.States; using Microsoft.Extensions.DependencyInjection; using System.Runtime.CompilerServices; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Implementations.Projections; @@ -15,16 +15,16 @@ public sealed class OneToOneProjection : IProjection, IState { public TimeStamp LastSourceAt { get; set; } - public required Pointer Pointer { get; set; } + public required StatePointer StatePointer { get; set; } - public static OneToOneProjection Construct(Pointer pointer) + public static OneToOneProjection Construct(StatePointer statePointer) { - return new OneToOneProjection { Pointer = pointer }; + return new OneToOneProjection { StatePointer = statePointer }; } - public Pointer GetPointer() + public StatePointer GetPointer() { - return Pointer; + return StatePointer; } public void Mutate(Source source) @@ -40,7 +40,7 @@ public void Mutate(Source source) mutator.Mutate(this); - Pointer = message.StatePointer; + StatePointer = message.StatePointer; } } @@ -58,7 +58,7 @@ public bool ShouldRecordAsLatest(OneToOneProjection? previousLatestState) public async IAsyncEnumerable EnumerateSources ( IServiceProvider serviceProvider, - Pointer projectionPointer, + StatePointer projectionPointer, [EnumeratorCancellation] CancellationToken cancellationToken ) { @@ -95,9 +95,9 @@ public static IEnumerable EnumerateRelevantStateIds(Source source) public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); - public OneToOneProjection WithVersion(Version version) + public OneToOneProjection WithVersion(StateVersion stateVersion) { - Pointer = Pointer.Id + version; + StatePointer = StatePointer.Id + stateVersion; return this; } diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs index b7aa2075..f4318133 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs @@ -1,5 +1,5 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Tests.Implementations.Entities.Deltas; namespace EntityDb.Common.Tests.Implementations.Seeders; diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs index 125707c7..79c4f8b0 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Common.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Seeders; diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs index 0c751b1e..9e41c4c4 100644 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Common.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Seeders; diff --git a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountLease.cs similarity index 57% rename from test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountLease.cs index f9a25e42..39d744ff 100644 --- a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountLease.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountLease.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; -namespace EntityDb.Common.Tests.Implementations.States.Attributes; +namespace EntityDb.Common.Tests.Implementations.Sources.Attributes; public sealed record CountLease(ulong Number) : ILease { diff --git a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountTag.cs similarity index 52% rename from test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountTag.cs index 3f1bd790..ad2f5eb7 100644 --- a/test/EntityDb.Common.Tests/Implementations/States/Attributes/CountTag.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountTag.cs @@ -1,6 +1,6 @@ -using EntityDb.Abstractions.States.Attributes; +using EntityDb.Abstractions.Sources.Attributes; -namespace EntityDb.Common.Tests.Implementations.States.Attributes; +namespace EntityDb.Common.Tests.Implementations.Sources.Attributes; public sealed record CountTag(ulong Number) : ITag { diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs index 478ccf69..4fac6d00 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs @@ -1,7 +1,7 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Common.Tests.Implementations.States.Attributes; +using EntityDb.Common.Tests.Implementations.Sources.Attributes; namespace EntityDb.Common.Tests.Implementations.Sources.Queries; diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs index 8f402c15..f5cc075b 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs @@ -1,11 +1,12 @@ -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public sealed record SourceIdDataQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, ILeaseDataQuery, +public sealed record SourceIdDataQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs index 4c1edc7f..f44f1896 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Tests.Implementations.Sources.Queries; diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs index d9425a7e..ecd1f036 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs @@ -1,7 +1,7 @@ -using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using EntityDb.Abstractions.ValueObjects; namespace EntityDb.Common.Tests.Implementations.Sources.Queries; diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs index f7b0726e..8e9ac2ae 100644 --- a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs @@ -1,11 +1,12 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.Sources.Queries.FilterBuilders; using EntityDb.Abstractions.Sources.Queries.SortBuilders; -using Version = EntityDb.Abstractions.ValueObjects.Version; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Tests.Implementations.Sources.Queries; -public sealed record StateVersionDataQuery(Version Gte, Version Lte, object? Options = null) : IMessageDataQuery, +public sealed record StateVersionDataQuery(StateVersion Gte, StateVersion Lte, object? Options = null) : + IMessageDataQuery, ILeaseDataQuery, ITagDataQuery { public TFilter GetFilter(ILeaseDataFilterBuilder builder) diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index 0eb81479..744cfbdd 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -1,13 +1,13 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Exceptions; using EntityDb.Common.Tests.Implementations.Seeders; using EntityDb.Common.Tests.Implementations.States; using Shouldly; using System.Diagnostics.CodeAnalysis; using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Projections; @@ -65,7 +65,7 @@ private async Task const uint replaceAtVersionValue = 3; TProjection.ShouldRecordAsLatestLogic.Value = (projection, _) => - projection.GetPointer().Version == new Version(replaceAtVersionValue); + projection.GetPointer().StateVersion == new StateVersion(replaceAtVersionValue); var stateId = Id.NewId(); @@ -95,9 +95,9 @@ private async Task // ASSERT - currentProjection.GetPointer().Version.Value.ShouldBe(numberOfVersions); + currentProjection.GetPointer().StateVersion.Value.ShouldBe(numberOfVersions); persistedProjection.ShouldNotBeNull(); - persistedProjection.GetPointer().Version.Value.ShouldBe(replaceAtVersionValue); + persistedProjection.GetPointer().StateVersion.Value.ShouldBe(replaceAtVersionValue); } [Theory] diff --git a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs index c177999c..8e5a68fb 100644 --- a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs +++ b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs @@ -1,5 +1,5 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Tests.Implementations.Seeders; using EntityDb.Common.Tests.Implementations.States; using Shouldly; @@ -54,7 +54,7 @@ private async Task committed.ShouldBeTrue(); persistedEntity.ShouldNotBeNull(); - persistedEntity.GetPointer().Version.Value.ShouldBe(numberOfVersions); + persistedEntity.GetPointer().StateVersion.Value.ShouldBe(numberOfVersions); } [Theory] diff --git a/test/EntityDb.Common.Tests/Sources/SourceTests.cs b/test/EntityDb.Common.Tests/Sources/SourceTests.cs index ab5dfbae..444839b9 100644 --- a/test/EntityDb.Common.Tests/Sources/SourceTests.cs +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -1,7 +1,8 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.States.Attributes; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Exceptions; using EntityDb.Common.Sources.Agents; using EntityDb.Common.Sources.Queries; @@ -9,8 +10,8 @@ using EntityDb.Common.Sources.Queries.Standard; using EntityDb.Common.Tests.Implementations.Entities.Deltas; using EntityDb.Common.Tests.Implementations.Seeders; +using EntityDb.Common.Tests.Implementations.Sources.Attributes; using EntityDb.Common.Tests.Implementations.Sources.Queries; -using EntityDb.Common.Tests.Implementations.States.Attributes; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; @@ -18,7 +19,6 @@ using Shouldly; using System.Diagnostics.CodeAnalysis; using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Sources; @@ -238,7 +238,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, { return sourceRepository .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) - .Select(pointer => pointer.Id); + .Select(statePointer => statePointer.Id); } } @@ -267,7 +267,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, { return sourceRepository .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) - .Select(pointer => pointer.Id); + .Select(statePointer => statePointer.Id); } } @@ -296,7 +296,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, { return sourceRepository .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) - .Select(pointer => pointer.Id); + .Select(statePointer => statePointer.Id); } } @@ -325,7 +325,7 @@ IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, { return sourceRepository .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) - .Select(pointer => pointer.Id); + .Select(statePointer => statePointer.Id); } } @@ -457,7 +457,7 @@ private static Source CreateSource .Select(versionNumber => new Message { Id = Id.NewId(), - StatePointer = nonNullableStateId + new Version(versionNumber), + StatePointer = nonNullableStateId + new StateVersion(versionNumber), Delta = new StoreNumber(versionNumber), }) .ToArray(), @@ -889,7 +889,7 @@ public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnot annotatedDelta.SourceId.ShouldBe(expectedSourceId); annotatedDelta.SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); annotatedDelta.StatePointer.Id.ShouldBe(expectedStateId); - annotatedDelta.StatePointer.Version.ShouldBe(new Version(number)); + annotatedDelta.StatePointer.StateVersion.ShouldBe(new StateVersion(number)); annotatedDelta.Data .ShouldBeAssignableTo() @@ -1064,7 +1064,7 @@ public async Task GivenMessageCommitted_WhenQueryingForVersionOne_ThenReturnTheE var source = CreateSource(new[] { 1ul }); - var versionOneQuery = new StateVersionDataQuery(new Version(1), new Version(1)); + var versionOneQuery = new StateVersionDataQuery(new StateVersion(1), new StateVersion(1)); // ACT @@ -1099,7 +1099,7 @@ public async Task GivenTwoMessagesCommitted_WhenQueryingForVersionTwo_ThenReturn var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); var secondSource = CreateSource(new[] { 2ul }, stateId: stateId); - var versionTwoQuery = new StateVersionDataQuery(new Version(2), new Version(2)); + var versionTwoQuery = new StateVersionDataQuery(new StateVersion(2), new StateVersion(2)); // ACT @@ -1419,7 +1419,7 @@ public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateVersion_ThenRet var sources = new List { source }; - var query = new StateVersionDataQuery(new Version(gte), new Version(lte)); + var query = new StateVersionDataQuery(new StateVersion(gte), new StateVersion(lte)); await PutSources(serviceScope, sources); await TestGetDeltas(serviceScope, query, expectedObjects); diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs index 3c145497..211da4a8 100644 --- a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -36,11 +36,13 @@ public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExcepti .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Setup(repository => + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock - .Setup(repository => repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) + .Setup(repository => + repository.EnumerateSourceIds(It.IsAny(), It.IsAny())) .Throws(new NotImplementedException()); sourceRepositoryMock diff --git a/test/EntityDb.Common.Tests/States/StateTests.cs b/test/EntityDb.Common.Tests/States/StateTests.cs index 2cf1c5b4..7c3cc5c6 100644 --- a/test/EntityDb.Common.Tests/States/StateTests.cs +++ b/test/EntityDb.Common.Tests/States/StateTests.cs @@ -1,5 +1,5 @@ -using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; using EntityDb.Common.Exceptions; using EntityDb.Common.Tests.Implementations.States; using Microsoft.Extensions.DependencyInjection; @@ -8,7 +8,6 @@ using Shouldly; using System.Diagnostics.CodeAnalysis; using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.States; @@ -36,7 +35,7 @@ private async Task await using var writeRepository = await GetWriteStateRepository(serviceScope); var stateId = Id.NewId(); - var expectedState = TState.Construct(stateId + new Version(300)); + var expectedState = TState.Construct(stateId + new StateVersion(300)); // ACT @@ -84,7 +83,7 @@ private async Task await using var readOnlyRepository = await GetReadOnlyStateRepository(serviceScope); - var stateSnapshot = TState.Construct(Id.NewId() + new Version(300)); + var stateSnapshot = TState.Construct(Id.NewId() + new StateVersion(300)); // ACT @@ -127,9 +126,9 @@ private async Task TState.ShouldRecordAsLatestLogic.Value = (_, _) => true; - Pointer latestPointer = Id.NewId(); + StatePointer latestPointer = Id.NewId(); - var stateSnapshot = TState.Construct(latestPointer.Id + new Version(5000)); + var stateSnapshot = TState.Construct(latestPointer.Id + new StateVersion(5000)); var persisted = await writeRepository.Put(latestPointer, stateSnapshot); @@ -180,7 +179,7 @@ private async Task Generic_GivenPersistedState_WhenReadInVariousReadModes_ThenRe var stateId = Id.NewId(); - var expectedSnapshot = TState.Construct(stateId + new Version(5000)); + var expectedSnapshot = TState.Construct(stateId + new StateVersion(5000)); var persisted = await writeRepository.Put(stateId, expectedSnapshot); diff --git a/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs index b637bc7b..a213d734 100644 --- a/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs @@ -1,6 +1,5 @@ using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.States; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.States; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -32,16 +31,16 @@ private async Task Generic_GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_Th var stateRepositoryMock = new Mock>(MockBehavior.Strict); stateRepositoryMock - .Setup(repository => repository.Get(It.IsAny(), It.IsAny())) + .Setup(repository => repository.Get(It.IsAny(), It.IsAny())) .ThrowsAsync(new NotImplementedException()); stateRepositoryMock .Setup(repository => - repository.Put(It.IsAny(), It.IsAny(), It.IsAny())) + repository.Put(It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new NotImplementedException()); stateRepositoryMock - .Setup(repository => repository.Delete(It.IsAny(), It.IsAny())) + .Setup(repository => repository.Delete(It.IsAny(), It.IsAny())) .ThrowsAsync(new NotImplementedException()); await using var serviceScope = CreateServiceScope(serviceCollection => diff --git a/test/EntityDb.Common.Tests/Streams/StreamTests.cs b/test/EntityDb.Common.Tests/Streams/StreamTests.cs index 49c17e0e..20623d92 100644 --- a/test/EntityDb.Common.Tests/Streams/StreamTests.cs +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -1,16 +1,17 @@ -using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; using EntityDb.Abstractions.Sources.Queries; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Extensions; using EntityDb.Common.Polyfills; +using EntityDb.Common.Sources.Attributes; using EntityDb.Common.Sources.Queries.Standard; -using EntityDb.Common.Streams; using EntityDb.Common.Tests.Implementations.Entities.Deltas; using Microsoft.Extensions.DependencyInjection; using Moq; using Shouldly; using Xunit; -using Version = EntityDb.Abstractions.ValueObjects.Version; namespace EntityDb.Common.Tests.Streams; @@ -37,14 +38,14 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc .InSequence(sequenceMock) .Setup(repository => repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerable.Empty()); + .Returns(AsyncEnumerable.Empty()); // Second query checks if message key lease already exists sourceRepositoryMock .InSequence(sequenceMock) .Setup(repository => repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerable.Empty()); + .Returns(AsyncEnumerable.Empty()); await using var serviceScope = CreateServiceScope(serviceCollection => { @@ -55,20 +56,20 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc await using var writeRepository = await GetWriteStreamRepository(serviceScope); - var streamKey = new Key("StreamKey"); - var expectedStreamKeyLease = MultipleStreamRepository.GetStreamKeyLease(streamKey); + IStateKey streamKey = new Key("StreamKey"); + var expectedStreamKeyLease = streamKey.ToLease(); - var messageKey = new Key("MessageKey"); - var expectedMessageKeyLease = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey); + IMessageKey messageKey = new Key("MessageKey"); + var expectedMessageKeyLease = messageKey.ToLease(streamKey); - var expectedDelta = new DoNothing(); + var expectedDelta = new DoNothingIdempotent(messageKey); // ACT await writeRepository.LoadOrCreate(streamKey); var staged = await writeRepository - .Append(streamKey, messageKey, expectedDelta); + .Append(streamKey, expectedDelta); var committed = await writeRepository .Commit(); @@ -80,11 +81,11 @@ public async Task GivenNewStreamMock_WhenStagingNewMessageKey_ThenCommittedSourc committedSources.Count.ShouldBe(1); committedSources[0].Messages.Length.ShouldBe(1); - committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.One); + committedSources[0].Messages[0].StatePointer.StateVersion.ShouldBe(StateVersion.One); committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); - committedSources[0].Messages[0].AddLeases.Count.ShouldBe(2); - committedSources[0].Messages[0].AddLeases.ElementAt(0).ShouldBe(expectedMessageKeyLease); - committedSources[0].Messages[0].AddLeases.ElementAt(1).ShouldBe(expectedStreamKeyLease); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(2); + committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedStreamKeyLease); + committedSources[0].Messages[0].AddLeases[1].ShouldBe(expectedMessageKeyLease); } [Theory] @@ -103,18 +104,18 @@ public async Task GivenNewStream_WhenStagingNewMessageKey_ThenCommitReturnsTrue( await using var writeRepository = await GetWriteStreamRepository(serviceScope); - var streamKey = new Key("StreamKey"); - var streamKeyLease = MultipleStreamRepository.GetStreamKeyLease(streamKey); + IStateKey streamKey = new Key("StreamKey"); + var streamKeyLease = streamKey.ToLease(); - var messageKey = new Key("MessageKey"); - var messageKeyLease = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey); + IMessageKey messageKey = new Key("MessageKey"); + var messageKeyLease = messageKey.ToLease(streamKey); // ACT await writeRepository.LoadOrCreate(streamKey); var staged = await writeRepository - .Append(streamKey, messageKey, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey)); var committed = await writeRepository.Commit(); @@ -134,7 +135,7 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted { // ARRANGE - var statePointer = Id.NewId() + Version.Zero.Next(); + var statePointer = Id.NewId() + StateVersion.One; var committedSources = new List(); @@ -153,7 +154,7 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted .InSequence(sequenceMock) .Setup(repository => repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerable.Empty()); + .Returns(AsyncEnumerable.Empty()); await using var serviceScope = CreateServiceScope(serviceCollection => { @@ -164,21 +165,21 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted await using var writeRepository = await GetWriteStreamRepository(serviceScope); - var streamKey = new Key("StreamKey"); - var messageKey = new Key("MessageKey"); + IStateKey streamKey = new Key("StreamKey"); + IMessageKey messageKey = new Key("MessageKey"); - var expectedLease = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey); + var expectedLease = messageKey.ToLease(streamKey); // ARRANGE ASSERTIONS - statePointer.Version.ShouldNotBe(Version.Zero); + statePointer.StateVersion.ShouldNotBe(StateVersion.Zero); // ACT await writeRepository.LoadOrCreate(streamKey); var staged = await writeRepository - .Append(streamKey, messageKey, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey)); var committed = await writeRepository.Commit(); @@ -190,9 +191,9 @@ public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommitted committedSources.Count.ShouldBe(1); committedSources[0].Messages.Length.ShouldBe(1); committedSources[0].Messages[0].StatePointer.Id.ShouldBe(statePointer.Id); - committedSources[0].Messages[0].StatePointer.Version.ShouldBe(Version.Zero); - committedSources[0].Messages[0].AddLeases.Count.ShouldBe(1); - committedSources[0].Messages[0].AddLeases.ElementAt(0).ShouldBe(expectedLease); + committedSources[0].Messages[0].StatePointer.StateVersion.ShouldBe(StateVersion.Zero); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); + committedSources[0].Messages[0].AddLeases[0].ShouldBe(expectedLease); } [Theory] @@ -211,19 +212,19 @@ public async Task GivenExistingStream_WhenStagingNewMessageKey_ThenCommitReturns await using var writeRepository = await GetWriteStreamRepository(serviceScope); - var streamKey = new Key("StreamKey"); - var streamKeyLease = MultipleStreamRepository.GetStreamKeyLease(streamKey); + IStateKey streamKey = new Key("StreamKey"); + var streamKeyLease = streamKey.ToLease(); - var messageKey1 = new Key("MessageKey1"); - var messageKeyLease1 = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey1); + IMessageKey messageKey1 = new Key("MessageKey1"); + var messageKeyLease1 = messageKey1.ToLease(streamKey); - var messageKey2 = new Key("MessageKey2"); - var messageKeyLease2 = MultipleStreamRepository.GetMessageKeyLease(streamKey, messageKey2); + IMessageKey messageKey2 = new Key("MessageKey2"); + var messageKeyLease2 = messageKey2.ToLease(streamKey); await writeRepository.LoadOrCreate(streamKey); var firstStaged = await writeRepository - .Append(streamKey, messageKey1, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey1)); var firstCommitted = await writeRepository.Commit(); @@ -235,7 +236,7 @@ public async Task GivenExistingStream_WhenStagingNewMessageKey_ThenCommitReturns // ACT var secondStaged = await writeRepository - .Append(streamKey, messageKey2, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey2)); var secondCommitted = await writeRepository.Commit(); @@ -255,7 +256,7 @@ public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenSta { // ARRANGE - var statePointer = Id.NewId() + Version.Zero.Next(); + var statePointer = Id.NewId() + StateVersion.One; var sequenceMock = new MockSequence(); var sourceRepositoryMock = new Mock(MockBehavior.Strict); @@ -283,15 +284,15 @@ public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenSta await using var writeRepository = await GetWriteStreamRepository(serviceScope); - var streamKey = new Key("StreamKey"); - var messageKey = new Key("MessageKey"); + IStateKey streamKey = new Key("StreamKey"); + IMessageKey messageKey = new Key("MessageKey"); await writeRepository.LoadOrCreate(streamKey); // ACT var staged = await writeRepository - .Append(streamKey, messageKey, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey)); // ASSERT @@ -314,13 +315,13 @@ public async Task GivenExistingStream_WhenStagingDuplicateMessageKey_ThenStagedR await using var writeRepository = await GetWriteStreamRepository(serviceScope); - var streamKey = new Key("StreamKey"); - var messageKey = new Key("MessageKey"); + IStateKey streamKey = new Key("StreamKey"); + IMessageKey messageKey = new Key("MessageKey"); await writeRepository.LoadOrCreate(streamKey); var stagedOnce = await writeRepository - .Append(streamKey, messageKey, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey)); var committedOnce = await writeRepository.Commit(); @@ -332,7 +333,7 @@ public async Task GivenExistingStream_WhenStagingDuplicateMessageKey_ThenStagedR // ACT var stagedTwice = await writeRepository - .Append(streamKey, messageKey, new DoNothing()); + .Append(streamKey, new DoNothingIdempotent(messageKey)); // ASSERT diff --git a/test/EntityDb.Common.Tests/TestsBase.cs b/test/EntityDb.Common.Tests/TestsBase.cs index 57494f1e..2778f81e 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -4,7 +4,6 @@ using EntityDb.Abstractions.Sources.Queries; using EntityDb.Abstractions.States; using EntityDb.Abstractions.Streams; -using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Disposables; using EntityDb.Common.Extensions; using EntityDb.Common.Polyfills; @@ -29,7 +28,6 @@ using Xunit.DependencyInjection; using Xunit.DependencyInjection.Logging; using Xunit.Sdk; -using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; namespace EntityDb.Common.Tests; @@ -540,7 +538,7 @@ protected static IStateRepositoryFactory GetMockedStateRepositoryFactor var stateRepositoryMock = new Mock>(MockBehavior.Strict); stateRepositoryMock - .Setup(repository => repository.Get(It.IsAny(), It.IsAny())) + .Setup(repository => repository.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(state); stateRepositoryMock @@ -621,7 +619,8 @@ public override async ValueTask DisposeAsync() } } - public sealed record SourceRepositoryAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, + public sealed record SourceRepositoryAdder(string Name, Type QueryOptionsType, + Func FixTimeStamp, AddDependenciesDelegate AddDependencies) { public override string ToString() @@ -646,7 +645,8 @@ public override string ToString() } } - public sealed record ProjectionRepositoryAdder(string Name, Type ProjectionType, AddDependenciesDelegate AddDependencies) + public sealed record ProjectionRepositoryAdder(string Name, Type ProjectionType, + AddDependenciesDelegate AddDependencies) { public override string ToString() { diff --git a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs index c95c0571..3b16926a 100644 --- a/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs +++ b/test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs @@ -1,5 +1,5 @@ using Bogus; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using Moq; From 8b46517b6c610d108a40bd14f2d1c3ca2ebd199d Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 18:47:49 -0800 Subject: [PATCH 86/93] Update StateDoesNotExistException.cs --- .../Exceptions/StateDoesNotExistException.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs index fce3bcda..ed7b2ab0 100644 --- a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs +++ b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs @@ -22,10 +22,14 @@ private StateDoesNotExistException(string message) : base(message) /// The state pointer found by a query. public static void ThrowIfNotAcceptable(StatePointer requestedStatePointer, StatePointer foundStatePointer) { - if (requestedStatePointer.StateVersion == StateVersion.Zero && - foundStatePointer.StateVersion == StateVersion.Zero) + if (requestedStatePointer.StateVersion == StateVersion.Zero) { - throw new StateDoesNotExistException("Requested latest but found none"); + if (foundStatePointer.StateVersion == StateVersion.Zero) + { + throw new StateDoesNotExistException("Requested latest but found none"); + } + + return; } if (requestedStatePointer != foundStatePointer) From bd1371d7d7544cfb395cbc3c427de8e109a78b38 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 14 Jan 2024 20:37:13 -0800 Subject: [PATCH 87/93] feat: Append that's not async not all appends are async.. wasted on non-async appends --- .../Streams/IMultipleStreamRepository.cs | 10 +++-- .../Streams/ISingleStreamRepository.cs | 5 ++- .../Streams/MultipleStreamRepository.cs | 39 +++++++++---------- .../Streams/SingleStreamRepository.cs | 9 ++++- src/EntityDb.Common/Streams/Stream.cs | 13 +++++++ 5 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs index 53a44811..ddcf0244 100644 --- a/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -27,6 +27,8 @@ public interface IMultipleStreamRepository : IDisposableResource /// A task. Task LoadOrCreate(IStateKey streamKey, CancellationToken cancellationToken = default); + void Append(IStateKey streamKey, object delta); + /// /// Stages a single delta with a given state key. /// @@ -34,11 +36,11 @@ public interface IMultipleStreamRepository : IDisposableResource /// The new delta that modifies the stream. /// A cancellation token. /// - /// Only async if the delta implements . - /// Only false if the the delta implements and - /// the message key already exists. + /// Only false if + /// returns a message key that already exists. /// - Task Append(IStateKey streamKey, object delta, CancellationToken cancellationToken = default); + Task Append(IStateKey streamKey, TDelta delta, CancellationToken cancellationToken = default) + where TDelta : IAddMessageKeyDelta; /// /// Commits all stages deltas. diff --git a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs index a137056a..098b7a75 100644 --- a/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs +++ b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs @@ -20,6 +20,8 @@ public interface ISingleStreamRepository : IDisposableResource /// IStateKey StreamKey { get; } + void Append(object delta); + /// /// Stages a single delta with a given state key and message key. /// @@ -30,7 +32,8 @@ public interface ISingleStreamRepository : IDisposableResource /// Only false if the the delta implements and /// the message key already exists. /// - Task Append(object delta, CancellationToken cancellationToken = default); + Task Append(TDelta delta, CancellationToken cancellationToken = default) + where TDelta : IAddMessageKeyDelta; /// /// Commits all stages deltas. diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index b713a2e0..529261b5 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -75,40 +75,39 @@ public void Create(IStateKey streamKey) _knownStreams.Add(streamKey, new Stream { Key = streamKey, Id = Id.NewId(), IsNew = true }); } - public async Task Append(IStateKey streamKey, object delta, CancellationToken cancellationToken = default) + public void Append(IStateKey streamKey, object delta) { if (!_knownStreams.TryGetValue(streamKey, out var stream)) { throw new UnknownStreamKeyException(); } - if (delta is IAddMessageKeyDelta addMessageKeyDelta) - { - var messageKeyLease = addMessageKeyDelta - .GetMessageKey() - .ToLease(streamKey); - - var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); + var nextStreamPointer = stream.GetNextPointer(); - if (streamPointer != default) - { - return false; - } + _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, streamKey)); + } + + public async Task Append(IStateKey streamKey, TDelta delta, CancellationToken cancellationToken = default) + where TDelta : IAddMessageKeyDelta + { + if (!_knownStreams.TryGetValue(streamKey, out var stream)) + { + throw new UnknownStreamKeyException(); } - StatePointer nextStreamPointer; + var messageKeyLease = delta + .GetMessageKey() + .ToLease(streamKey); - if (stream.IsNew) - { - _knownStreams[streamKey].IsNew = false; + var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); - nextStreamPointer = stream.Id + StateVersion.One; - } - else + if (streamPointer != default) { - nextStreamPointer = stream.Id; + return false; } + var nextStreamPointer = stream.GetNextPointer(); + _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, streamKey)); return true; diff --git a/src/EntityDb.Common/Streams/SingleStreamRepository.cs b/src/EntityDb.Common/Streams/SingleStreamRepository.cs index 59be1318..7ce58e8a 100644 --- a/src/EntityDb.Common/Streams/SingleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/SingleStreamRepository.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions.Sources; using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; using EntityDb.Abstractions.Streams; using EntityDb.Common.Disposables; @@ -19,7 +20,13 @@ public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository public ISourceRepository SourceRepository => _multipleStreamRepository.SourceRepository; public IStateKey StreamKey { get; } - public Task Append(object delta, CancellationToken cancellationToken = default) + public void Append(object delta) + { + _multipleStreamRepository.Append(StreamKey, delta); + } + + public Task Append(TDelta delta, CancellationToken cancellationToken = default) + where TDelta : IAddMessageKeyDelta { return _multipleStreamRepository.Append(StreamKey, delta, cancellationToken); } diff --git a/src/EntityDb.Common/Streams/Stream.cs b/src/EntityDb.Common/Streams/Stream.cs index 7b42b9a2..b6894240 100644 --- a/src/EntityDb.Common/Streams/Stream.cs +++ b/src/EntityDb.Common/Streams/Stream.cs @@ -1,5 +1,6 @@ using EntityDb.Abstractions; using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; using EntityDb.Abstractions.Streams; namespace EntityDb.Common.Streams; @@ -9,4 +10,16 @@ internal sealed class Stream : IStream public required bool IsNew { get; set; } public required IStateKey Key { get; init; } public required Id Id { get; init; } + + public StatePointer GetNextPointer() + { + if (IsNew) + { + IsNew = false; + + return Id + StateVersion.One; + } + + return Id; + } } From 72b3b03180234888d6ae76ed118f2f5920991a19 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Wed, 17 Jan 2024 22:56:01 -0800 Subject: [PATCH 88/93] fix --- .../Entities/IMultipleEntityRepository.cs | 2 +- .../Projections/IProjection.cs | 2 +- .../Projections/IProjectionRepository.cs | 4 +- src/EntityDb.Abstractions/States/IState.cs | 5 +- .../States/StatePointer.cs | 28 +++++++++ .../States/StateVersion.cs | 9 +++ .../Entities/EntityRepositoryFactory.cs | 2 +- .../Entities/MultipleEntityRepository.cs | 55 +++++++++++------ .../Exceptions/ExistingEntityException.cs | 2 +- .../Exceptions/StateDoesNotExistException.cs | 40 ------------- ...Exception.cs => UnknownEntityException.cs} | 2 +- .../Exceptions/UnknownProjectionException.cs | 3 + .../Projections/ProjectionRepository.cs | 51 ++++++++++++++-- .../Processors/EntityStateSourceProcessor.cs | 60 ++++++++++++------- .../ProjectionStateSourceProcessor.cs | 27 ++++----- .../Queries/Standard/GetDeltasDataQuery.cs | 4 +- .../ProjectionStateSourceSubscriber.cs | 2 +- .../Entities/EntityRepositoryTests.cs | 49 +-------------- .../Entities/EntityTests.cs | 10 ++-- .../Implementations/Entities/TestEntity.cs | 19 +++--- .../Projections/OneToOneProjection.cs | 26 +++----- .../States/IStateWithTestLogic.cs | 4 +- .../Projections/ProjectionsTests.cs | 21 ++++--- .../EntityStateSourceSubscriberTests.cs | 12 ++-- .../States/StateTests.cs | 2 +- 25 files changed, 226 insertions(+), 215 deletions(-) delete mode 100644 src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs rename src/EntityDb.Common/Exceptions/{UnknownEntityIdException.cs => UnknownEntityException.cs} (85%) create mode 100644 src/EntityDb.Common/Exceptions/UnknownProjectionException.cs diff --git a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs index 9b4cdd29..f03f6242 100644 --- a/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs +++ b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs @@ -39,7 +39,7 @@ public interface IMultipleEntityRepository : IDisposableResource /// Call this method to load an entity that already exists before calling /// . /// - Task Load(StatePointer entityPointer, CancellationToken cancellationToken = default); + Task TryLoad(StatePointer entityPointer, CancellationToken cancellationToken = default); /// /// Returns the state of a for a given . diff --git a/src/EntityDb.Abstractions/Projections/IProjection.cs b/src/EntityDb.Abstractions/Projections/IProjection.cs index 96b1683c..dde3525b 100644 --- a/src/EntityDb.Abstractions/Projections/IProjection.cs +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -35,5 +35,5 @@ IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, Stat /// /// A source /// The state ids for the projections. - static abstract IEnumerable EnumerateRelevantStateIds(Source source); + static abstract IEnumerable EnumerateProjectionIds(Source source); } diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index f0ae7298..f898aac8 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -20,5 +20,7 @@ public interface IProjectionRepository : IDisposableResource /// The state pointer to the projection. /// A cancellation token. /// The state of a for . - Task Get(StatePointer projectionPointer, CancellationToken cancellationToken = default); + Task TryLoad(StatePointer projectionPointer, CancellationToken cancellationToken = default); + + TProjection Get(Id projectionId); } diff --git a/src/EntityDb.Abstractions/States/IState.cs b/src/EntityDb.Abstractions/States/IState.cs index a1823154..cba8e19a 100644 --- a/src/EntityDb.Abstractions/States/IState.cs +++ b/src/EntityDb.Abstractions/States/IState.cs @@ -27,12 +27,11 @@ public interface IState /// You would use this if you intent to fetch a state at multiple versions and don't want to hit /// the source database when it can be avoided. /// - bool ShouldRecord(); + bool ShouldPersist() => false; /// /// Indicates if this state instance should be recorded as the latest state. /// - /// The previous instance of the latest state. /// true if this state instance should be recorded as the latest state, or else false. - bool ShouldRecordAsLatest(TState? previousLatestState); + bool ShouldPersistAsLatest() => false; } diff --git a/src/EntityDb.Abstractions/States/StatePointer.cs b/src/EntityDb.Abstractions/States/StatePointer.cs index 2db85c66..d7fa3e52 100644 --- a/src/EntityDb.Abstractions/States/StatePointer.cs +++ b/src/EntityDb.Abstractions/States/StatePointer.cs @@ -16,6 +16,34 @@ public override string ToString() { return $"{Id}@{StateVersion}"; } + + /// + /// Checks if the state pointer found satisfies the state pointer requested. + /// + /// A candidate state pointer. + public bool IsSatisfiedBy(StatePointer candidateStatePointer) + { + if (Id != candidateStatePointer.Id) + { + return false; + } + + if (StateVersion == StateVersion.Zero) + { + return candidateStatePointer.StateVersion != StateVersion.Zero; + } + + return StateVersion == candidateStatePointer.StateVersion; + } + + /// + /// Returns the next state pointer. + /// + /// The next state pointer. + public StatePointer Previous() + { + return Id + StateVersion.Previous(); + } /// /// Returns the next state pointer. diff --git a/src/EntityDb.Abstractions/States/StateVersion.cs b/src/EntityDb.Abstractions/States/StateVersion.cs index cb652737..c1bf36cd 100644 --- a/src/EntityDb.Abstractions/States/StateVersion.cs +++ b/src/EntityDb.Abstractions/States/StateVersion.cs @@ -18,6 +18,15 @@ public readonly record struct StateVersion(ulong Value) /// public static readonly StateVersion One = new(1); + /// + /// Returns the next version. + /// + /// The next version. + public StateVersion Previous() + { + return new StateVersion(Value - 1); + } + /// /// Returns the next version. /// diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index 13dffdac..c7a9b484 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -54,7 +54,7 @@ public async Task> CreateSingleForExisting var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, stateSessionOptionsName, cancellationToken); - await multipleEntityRepository.Load(entityPointer, cancellationToken); + await multipleEntityRepository.TryLoad(entityPointer, cancellationToken); return new SingleEntityRepository(multipleEntityRepository, entityPointer); } diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs index faeb3caa..b00e7f7b 100644 --- a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -48,41 +48,60 @@ public void Create(Id entityId) _knownEntities.Add(entityId, entity); } - public async Task Load(StatePointer entityPointer, CancellationToken cancellationToken = default) + public async Task TryLoad(StatePointer entityPointer, CancellationToken cancellationToken = default) { - if (_knownEntities.ContainsKey(entityPointer.Id)) + var entityId = entityPointer.Id; + + if (_knownEntities.TryGetValue(entityId, out var entity)) { - throw new ExistingEntityException(); + var knownEntityPointer = entity.GetPointer(); + + if (entityPointer.IsSatisfiedBy(knownEntityPointer)) + { + return true; + } + + if (entityPointer.StateVersion.Value < knownEntityPointer.StateVersion.Value) + { + return false; + } + } + else if (StateRepository is not null) + { + entity = await StateRepository.Get(entityPointer, cancellationToken) ?? + TEntity.Construct(entityId); + } + else + { + entity = TEntity.Construct(entityId); } - var state = StateRepository is not null - ? await StateRepository.Get(entityPointer, cancellationToken) ?? - TEntity.Construct(entityPointer.Id) - : TEntity.Construct(entityPointer.Id); - - var statePointer = state.GetPointer(); - - var query = new GetDeltasDataQuery(entityPointer, statePointer.StateVersion); + var query = new GetDeltasDataQuery(entityPointer, entity.GetPointer().StateVersion); - var entity = await SourceRepository + entity = await SourceRepository .EnumerateDeltas(query, cancellationToken) .AggregateAsync ( - state, - (current, delta) => current.Reduce(delta), + entity, + (previousEntity, delta) => previousEntity.Reduce(delta), cancellationToken ); - StateDoesNotExistException.ThrowIfNotAcceptable(entityPointer, entity.GetPointer()); + if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) + { + return false; + } - _knownEntities.Add(entityPointer.Id, entity); + _knownEntities.Add(entityId, entity); + + return true; } public TEntity Get(Id entityId) { if (!_knownEntities.TryGetValue(entityId, out var entity)) { - throw new UnknownEntityIdException(); + throw new UnknownEntityException(); } return entity; @@ -92,7 +111,7 @@ public void Append(Id entityId, object delta) { if (!_knownEntities.TryGetValue(entityId, out var entity)) { - throw new UnknownEntityIdException(); + throw new UnknownEntityException(); } entity = entity.Reduce(delta); diff --git a/src/EntityDb.Common/Exceptions/ExistingEntityException.cs b/src/EntityDb.Common/Exceptions/ExistingEntityException.cs index 183c1506..4f5ef5a3 100644 --- a/src/EntityDb.Common/Exceptions/ExistingEntityException.cs +++ b/src/EntityDb.Common/Exceptions/ExistingEntityException.cs @@ -4,7 +4,7 @@ namespace EntityDb.Common.Exceptions; /// /// The exception that is thrown when or -/// is called for an entity that already +/// is called for an entity that already /// exists in the repository. /// public sealed class ExistingEntityException : Exception; diff --git a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs b/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs deleted file mode 100644 index ed7b2ab0..00000000 --- a/src/EntityDb.Common/Exceptions/StateDoesNotExistException.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.States; - -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when , -/// , or -/// cannot find the requested state. -/// -public sealed class StateDoesNotExistException : Exception -{ - private StateDoesNotExistException(string message) : base(message) - { - } - - /// - /// Checks if the state pointer found satisfies the state pointer requested. - /// - /// The state pointer requested by the agent. - /// The state pointer found by a query. - public static void ThrowIfNotAcceptable(StatePointer requestedStatePointer, StatePointer foundStatePointer) - { - if (requestedStatePointer.StateVersion == StateVersion.Zero) - { - if (foundStatePointer.StateVersion == StateVersion.Zero) - { - throw new StateDoesNotExistException("Requested latest but found none"); - } - - return; - } - - if (requestedStatePointer != foundStatePointer) - { - throw new StateDoesNotExistException($"Requested {requestedStatePointer} but found {foundStatePointer}."); - } - } -} diff --git a/src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs b/src/EntityDb.Common/Exceptions/UnknownEntityException.cs similarity index 85% rename from src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs rename to src/EntityDb.Common/Exceptions/UnknownEntityException.cs index 2a729cb9..9a2dfa2f 100644 --- a/src/EntityDb.Common/Exceptions/UnknownEntityIdException.cs +++ b/src/EntityDb.Common/Exceptions/UnknownEntityException.cs @@ -7,4 +7,4 @@ namespace EntityDb.Common.Exceptions; /// is called for an entity that /// is not loaded into the repository. /// -public sealed class UnknownEntityIdException : Exception; +public sealed class UnknownEntityException : Exception; diff --git a/src/EntityDb.Common/Exceptions/UnknownProjectionException.cs b/src/EntityDb.Common/Exceptions/UnknownProjectionException.cs new file mode 100644 index 00000000..aab82ddf --- /dev/null +++ b/src/EntityDb.Common/Exceptions/UnknownProjectionException.cs @@ -0,0 +1,3 @@ +namespace EntityDb.Common.Exceptions; + +public sealed class UnknownProjectionException : Exception; diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index 42f26602..8156ff63 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,8 +1,10 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Projections; using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; namespace EntityDb.Common.Projections; @@ -11,6 +13,7 @@ internal sealed class ProjectionRepository : DisposableResourceBase where TProjection : IProjection { private readonly IServiceProvider _serviceProvider; + private readonly Dictionary _knownProjections = new(); public ProjectionRepository ( @@ -24,12 +27,33 @@ public ProjectionRepository public IStateRepository? StateRepository { get; } - public async Task Get(StatePointer projectionPointer, CancellationToken cancellationToken = default) + public async Task TryLoad(StatePointer projectionPointer, CancellationToken cancellationToken = default) { - var projection = StateRepository is not null - ? await StateRepository.Get(projectionPointer, cancellationToken) ?? - TProjection.Construct(projectionPointer.Id) - : TProjection.Construct(projectionPointer.Id); + var projectionId = projectionPointer.Id; + + if (_knownProjections.TryGetValue(projectionId, out var projection)) + { + var knownProjectionPointer = projection.GetPointer(); + + if (projectionPointer.IsSatisfiedBy(knownProjectionPointer)) + { + return true; + } + + if (projectionPointer.StateVersion.Value < knownProjectionPointer.StateVersion.Value) + { + return false; + } + } + else if (StateRepository is not null) + { + projection = await StateRepository.Get(projectionPointer, cancellationToken) ?? + TProjection.Construct(projectionId); + } + else + { + projection = TProjection.Construct(projectionId); + } var sources = projection.EnumerateSources(_serviceProvider, projectionPointer, cancellationToken); @@ -38,7 +62,22 @@ public async Task Get(StatePointer projectionPointer, CancellationT projection.Mutate(source); } - StateDoesNotExistException.ThrowIfNotAcceptable(projectionPointer, projection.GetPointer()); + if (!projectionPointer.IsSatisfiedBy(projection.GetPointer())) + { + return false; + } + + _knownProjections.Add(projectionId, projection); + + return true; + } + + public TProjection Get(Id projectionId) + { + if (!_knownProjections.TryGetValue(projectionId, out var projection)) + { + throw new UnknownProjectionException(); + } return projection; } diff --git a/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs index 751ca8d4..ab6e2b52 100644 --- a/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs @@ -37,7 +37,7 @@ string stateSessionOptionsName /// public async Task Process(Source source, CancellationToken cancellationToken) { - if (!source.Messages.Any(message => TEntity.CanReduce(message.Delta))) + if (source.Messages.Any(message => !TEntity.CanReduce(message.Delta))) { throw new NotSupportedException(); } @@ -52,38 +52,52 @@ public async Task Process(Source source, CancellationToken cancellationToken) return; } - var latestEntities = new Dictionary(); - var saveEntities = new Dictionary(); + var persistEntities = new Dictionary(); - foreach (var message in source.Messages) - { - var entityPointer = message.StatePointer; + var entityMessageGroups = source.Messages + .GroupBy(message => message.StatePointer.Id); - if (!latestEntities.Remove(entityPointer.Id, out var previousEntity)) + foreach (var entityMessageGroup in entityMessageGroups) + { + var entityId = entityMessageGroup.Key; + var messages = entityMessageGroup.ToArray(); + + StatePointer previousEntityPointer = messages[0].StatePointer.Previous(); + + TEntity previousEntity; + + if (previousEntityPointer.StateVersion == StateVersion.Zero) { - previousEntity = - await entityRepository.StateRepository.Get(entityPointer, cancellationToken); + previousEntity = TEntity.Construct(entityId); } - - var nextEntity = (previousEntity ?? TEntity.Construct(entityPointer.Id)).Reduce(message.Delta); - var nextEntityPointer = nextEntity.GetPointer(); - - if (nextEntity.ShouldRecordAsLatest(previousEntity)) + else if (await entityRepository.TryLoad(previousEntityPointer, cancellationToken)) { - saveEntities[entityPointer.Id] = nextEntity; + previousEntity = entityRepository.Get(entityId); } - - if (nextEntity.ShouldRecord()) + else { - saveEntities[nextEntityPointer] = nextEntity; + throw new NotSupportedException(); + } + + foreach (var message in messages) + { + var nextEntity = previousEntity.Reduce(message.Delta); + + if (nextEntity.ShouldPersist()) + { + persistEntities[nextEntity.GetPointer()] = nextEntity; + } + + if (nextEntity.ShouldPersistAsLatest()) + { + persistEntities[entityId] = nextEntity; + } + + previousEntity = nextEntity; } - - latestEntities[entityPointer.Id] = nextEntity; - - cancellationToken.ThrowIfCancellationRequested(); } - foreach (var (entityPointer, entity) in saveEntities) + foreach (var (entityPointer, entity) in persistEntities) { await entityRepository.StateRepository.Put(entityPointer, entity, cancellationToken); } diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs index 429f8625..ca69fdea 100644 --- a/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs +++ b/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs @@ -42,29 +42,22 @@ public async Task Process(Source source, CancellationToken cancellationToken) return; } - foreach (var stateId in TProjection.EnumerateRelevantStateIds(source)) + foreach (var projectionId in TProjection.EnumerateProjectionIds(source).Distinct()) { - var previousProjection = await projectionRepository.StateRepository - .Get(stateId, cancellationToken); + var projection = await projectionRepository.TryLoad(projectionId, cancellationToken) + ? projectionRepository.Get(projectionId) + : TProjection.Construct(projectionId); + + projection.Mutate(source); - var nextProjection = previousProjection == null - ? TProjection.Construct(stateId) - : previousProjection; - - nextProjection.Mutate(source); - - var nextProjectionPointer = nextProjection.GetPointer(); - - if (nextProjection.ShouldRecordAsLatest(previousProjection)) + if (projection.ShouldPersist()) { - await projectionRepository.StateRepository.Put(nextProjectionPointer.Id, nextProjection, - cancellationToken); + await projectionRepository.StateRepository.Put(projection.GetPointer(), projection, cancellationToken); } - if (nextProjection.ShouldRecord()) + if (projection.ShouldPersistAsLatest()) { - await projectionRepository.StateRepository.Put(nextProjectionPointer, nextProjection, - cancellationToken); + await projectionRepository.StateRepository.Put(projectionId, projection, cancellationToken); } cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs index 910df225..d34344ba 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs @@ -5,14 +5,14 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetDeltasDataQuery(StatePointer StatePointer, StateVersion PersistedStateStateVersion, +internal sealed record GetDeltasDataQuery(StatePointer StatePointer, StateVersion KnownStateVersion, object? Options = null) : IMessageDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { var filters = new List { - builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(PersistedStateStateVersion.Next()), + builder.StateIdIn(StatePointer.Id), builder.StateVersionGte(KnownStateVersion.Next()), }; if (StatePointer.StateVersion != StateVersion.Zero) diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs index 7202f4b4..ae5468c4 100644 --- a/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs @@ -17,7 +17,7 @@ public ProjectionStateSourceSubscriber(ISourceProcessorQueue sourceProcessorQueu public void Notify(Source source) { - if (!TProjection.EnumerateRelevantStateIds(source).Any()) + if (!TProjection.EnumerateProjectionIds(source).Any()) { return; } diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs index 60eae4bc..30f02af2 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -38,7 +38,7 @@ private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow(() => entityRepository.Get(default)); + Should.Throw(() => entityRepository.Get(default)); } private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity( @@ -110,41 +110,6 @@ private async Task Generic_GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_Th committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); } - private async Task Generic_GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( - SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) - where TEntity : IEntity - { - // ARRANGE - - await using var serviceScope = CreateServiceScope(serviceCollection => - { - sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); - entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); - }); - - await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); - await using var readRepository = await GetReadOnlyEntityRepository(serviceScope, false); - - var entityId = Id.NewId(); - - writeRepository.Create(entityId); - writeRepository.Append(entityId, new DoNothing()); - - var committed = await writeRepository.Commit(); - - // ARRANGE ASSERTIONS - - committed.ShouldBeTrue(); - - // ACT - - await readRepository.Load(entityId); - - // ASSERT - - await Should.ThrowAsync(readRepository.Load(entityId)); - } - private async Task Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) where TEntity : IEntity @@ -260,18 +225,6 @@ public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAdd ); } - [Theory] - [MemberData(nameof(With_Source_Entity))] - public Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows( - SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) - { - return RunGenericTestAsync - ( - new[] { entityRepositoryAdder.EntityType }, - new object?[] { sourceRepositoryAdder, entityRepositoryAdder } - ); - } - [Theory] [MemberData(nameof(With_Source_Entity))] public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 3f2fd25a..bad8e535 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -60,7 +60,7 @@ private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenRe // ACT - await readOnlyRepository.Load(entityId + new StateVersion(m)); + await readOnlyRepository.TryLoad(entityId + new StateVersion(m)); var entity = readOnlyRepository.Get(entityId); @@ -132,7 +132,7 @@ private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEn // ACT - await readOnlyRepository.Load(entityId); + await readOnlyRepository.TryLoad(entityId); var entity = readOnlyRepository.Get(entityId); @@ -255,7 +255,7 @@ private async Task // ACT - await entityRepository.Load(default); + await entityRepository.TryLoad(default); var currentEntity = entityRepository.Get(default); @@ -283,7 +283,7 @@ private async Task Generic_GivenNonExistentEntityId_WhenGettingCurrentEntity_The // ASSERT - Should.Throw(() => entityRepository.Get(default)); + Should.Throw(() => entityRepository.Get(default)); } private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity( @@ -320,7 +320,7 @@ private async Task Generic_GivenEntityCommitted_WhenGettingEntity_ThenReturnEnti // ACT - await entityRepository.Load(entityId); + await entityRepository.TryLoad(entityId); var actualEntity = entityRepository.Get(entityId); diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index e999a87f..2f7c472f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -34,26 +34,21 @@ public TestEntity Reduce(object delta) throw new NotSupportedException(); } - public bool ShouldRecord() + public bool ShouldPersist() { - return ShouldRecordLogic.Value is not null && ShouldRecordLogic.Value.Invoke(this); + return ShouldPersistLogic.Value is not null && ShouldPersistLogic.Value.Invoke(this); } - public bool ShouldRecordAsLatest(TestEntity? previousLatestState) + public bool ShouldPersistAsLatest() { - return ShouldRecordAsLatestLogic.Value is not null && - ShouldRecordAsLatestLogic.Value.Invoke(this, previousLatestState); + return ShouldPersistAsLatestLogic.Value is not null && + ShouldPersistAsLatestLogic.Value.Invoke(this); } public static string MongoDbCollectionName => "TestEntities"; public static string RedisKeyNamespace => "test-entity"; - public static AsyncLocal?> ShouldRecordLogic { get; } = new(); + public static AsyncLocal?> ShouldPersistLogic { get; } = new(); - public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); - - public TestEntity WithVersion(StateVersion stateVersion) - { - return new TestEntity { StatePointer = StatePointer.Id + stateVersion }; - } + public static AsyncLocal?> ShouldPersistAsLatestLogic { get; } = new(); } diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index 0bcedae0..f51135e1 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -44,15 +44,15 @@ public void Mutate(Source source) } } - public bool ShouldRecord() + public bool ShouldPersist() { - return ShouldRecordLogic.Value is not null && ShouldRecordLogic.Value.Invoke(this); + return ShouldPersistLogic.Value is not null && ShouldPersistLogic.Value.Invoke(this); } - public bool ShouldRecordAsLatest(OneToOneProjection? previousLatestState) + public bool ShouldPersistAsLatest() { - return ShouldRecordAsLatestLogic.Value is not null && - ShouldRecordAsLatestLogic.Value.Invoke(this, previousLatestState); + return ShouldPersistAsLatestLogic.Value is not null && + ShouldPersistAsLatestLogic.Value.Invoke(this); } public async IAsyncEnumerable EnumerateSources @@ -79,26 +79,18 @@ [EnumeratorCancellation] CancellationToken cancellationToken } } - public static IEnumerable EnumerateRelevantStateIds(Source source) + public static IEnumerable EnumerateProjectionIds(Source source) { return source.Messages .Where(message => message.Delta is IMutator) - .Select(message => message.StatePointer.Id) - .Distinct(); + .Select(message => message.StatePointer.Id); } public static string MongoDbCollectionName => "OneToOneProjections"; public static string RedisKeyNamespace => "one-to-one-projection"; - public static AsyncLocal?> ShouldRecordLogic { get; } = new(); + public static AsyncLocal?> ShouldPersistLogic { get; } = new(); - public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = + public static AsyncLocal?> ShouldPersistAsLatestLogic { get; } = new(); - - public OneToOneProjection WithVersion(StateVersion stateVersion) - { - StatePointer = StatePointer.Id + stateVersion; - - return this; - } } diff --git a/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs b/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs index 8d7ffbd4..7356bea6 100644 --- a/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs +++ b/test/EntityDb.Common.Tests/Implementations/States/IStateWithTestLogic.cs @@ -7,6 +7,6 @@ public interface IStateWithTestLogic : IState { static abstract string MongoDbCollectionName { get; } static abstract string RedisKeyNamespace { get; } - static abstract AsyncLocal?> ShouldRecordLogic { get; } - static abstract AsyncLocal?> ShouldRecordAsLatestLogic { get; } + static abstract AsyncLocal?> ShouldPersistLogic { get; } + static abstract AsyncLocal?> ShouldPersistAsLatestLogic { get; } } diff --git a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index 744cfbdd..41e47f3f 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -20,7 +20,7 @@ public ProjectionsTests(IServiceProvider startupServiceProvider, DatabaseContain { } - private async Task Generic_GivenEmptySourceRepository_WhenGettingProjection_ThenThrow( + private async Task Generic_GivenEmptySourceRepository_WhenLoadingProjection_ThenReturnFalse( SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, StateRepositoryAdder projectionStateRepositoryAdder) where TProjection : IProjection @@ -36,10 +36,13 @@ private async Task Generic_GivenEmptySourceRepository_WhenGettingProjection_Then await using var readOnlyRepository = await GetReadOnlyProjectionRepository(serviceScope, true); - // ACT & ASSERT + // ACT + + var loaded = await readOnlyRepository.TryLoad(default); + + // ASSERT - await Should.ThrowAsync(() => - readOnlyRepository.Get(default)); + loaded.ShouldBeFalse(); } private async Task @@ -64,7 +67,7 @@ private async Task const uint numberOfVersions = 5; const uint replaceAtVersionValue = 3; - TProjection.ShouldRecordAsLatestLogic.Value = (projection, _) => + TProjection.ShouldPersistAsLatestLogic.Value = projection => projection.GetPointer().StateVersion == new StateVersion(replaceAtVersionValue); var stateId = Id.NewId(); @@ -89,8 +92,10 @@ private async Task projectionRepository.StateRepository.ShouldNotBeNull(); // ACT - - var currentProjection = await projectionRepository.Get(stateId); + + await projectionRepository.TryLoad(stateId); + + var currentProjection = projectionRepository.Get(stateId); var persistedProjection = await projectionRepository.StateRepository.Get(stateId); // ASSERT @@ -102,7 +107,7 @@ private async Task [Theory] [MemberData(nameof(With_Source_EntityState_ProjectionState))] - public Task GivenEmptySourceRepository_WhenGettingProjection_ThenThrow(SourceRepositoryAdder sourceRepositoryAdder, + public Task GivenEmptySourceRepository_WhenLoadingProjection_ThenReturnFalse(SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, StateRepositoryAdder projectionStateRepositoryAdder) { return RunGenericTestAsync diff --git a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs index 8e5a68fb..952de70e 100644 --- a/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs +++ b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs @@ -18,7 +18,7 @@ public EntityStateSourceSubscriberTests(IServiceProvider startupServiceProvider, } private async Task - Generic_GivenStateShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState< + Generic_GivenStateShouldPersistAsLatestAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState< TEntity>( SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder stateRepositoryAdder) where TEntity : class, IEntity, IStateWithTestLogic @@ -35,7 +35,7 @@ private async Task await using var stateRepository = await GetReadOnlyStateRepository(serviceScope); - TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => true; + TEntity.ShouldPersistAsLatestLogic.Value = _ => true; var entityId = Id.NewId(); @@ -60,7 +60,7 @@ private async Task [Theory] [MemberData(nameof(With_Source_EntityState))] private Task - GivenStateShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState( + GivenStateShouldPersistAsLatestAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState( SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) { return RunGenericTestAsync @@ -71,7 +71,7 @@ private Task } private async Task - Generic_GivenStateShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntityStateSourceSubscriber_ThenNeverPersistState< + Generic_GivenStateShouldPersistAsLatestAlwaysReturnsFalse_WhenRunningEntityStateSourceSubscriber_ThenNeverPersistState< TEntity>(SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder stateRepositoryAdder) where TEntity : class, IEntity, IStateWithTestLogic { @@ -86,7 +86,7 @@ private async Task await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); await using var stateRepository = await GetReadOnlyStateRepository(serviceScope); - TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => false; + TEntity.ShouldPersistAsLatestLogic.Value = _ => false; var entityId = Id.NewId(); @@ -107,7 +107,7 @@ private async Task [Theory] [MemberData(nameof(With_Source_EntityState))] private Task - GivenStateShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntityStateSourceSubscriber_ThenNeverPersistState( + GivenStateShouldPersistAsLatestAlwaysReturnsFalse_WhenRunningEntityStateSourceSubscriber_ThenNeverPersistState( SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) { return RunGenericTestAsync diff --git a/test/EntityDb.Common.Tests/States/StateTests.cs b/test/EntityDb.Common.Tests/States/StateTests.cs index 7c3cc5c6..42ae991f 100644 --- a/test/EntityDb.Common.Tests/States/StateTests.cs +++ b/test/EntityDb.Common.Tests/States/StateTests.cs @@ -124,7 +124,7 @@ private async Task await using var writeRepository = await GetWriteStateRepository(serviceScope); - TState.ShouldRecordAsLatestLogic.Value = (_, _) => true; + TState.ShouldPersistAsLatestLogic.Value = _ => true; StatePointer latestPointer = Id.NewId(); From 37d325dd1c0cf377fccc43b1893d8f2b6b3edb2c Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Thu, 18 Jan 2024 20:00:45 -0800 Subject: [PATCH 89/93] fix:allow for a conditional message key stripe test mode webhook doesn't send an idempotency key :/ --- src/EntityDb.Abstractions/Sources/Message.cs | 6 ++---- .../States/Deltas/IAddMessageKeyDelta.cs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs index 60b195f1..23978b49 100644 --- a/src/EntityDb.Abstractions/Sources/Message.cs +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -70,12 +70,10 @@ internal static Message NewMessage .Select(key => key.ToLease())); } - if (delta is IAddMessageKeyDelta addMessageKeyDelta) + if (delta is IAddMessageKeyDelta addMessageKeyDelta && addMessageKeyDelta.GetMessageKey() is { } messageKey) { addLeases = addLeases - .AppendOrStart(addMessageKeyDelta - .GetMessageKey() - .ToLease(stateKey)); + .AppendOrStart(messageKey.ToLease(stateKey)); } } else if (delta is IAddLeasesDelta addLeasesDelta) diff --git a/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs index 947d6b40..b14671d0 100644 --- a/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs +++ b/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs @@ -4,5 +4,5 @@ namespace EntityDb.Abstractions.States.Deltas; public interface IAddMessageKeyDelta { - IMessageKey GetMessageKey(); + IMessageKey? GetMessageKey(); } From 6338b6c91694f271a39b3b9d98257a93a4135948 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sat, 20 Jan 2024 11:12:31 -0800 Subject: [PATCH 90/93] fix: allow null --- .../Streams/MultipleStreamRepository.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs index 529261b5..4f7b0de4 100644 --- a/src/EntityDb.Common/Streams/MultipleStreamRepository.cs +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -95,15 +95,16 @@ public async Task Append(IStateKey streamKey, TDelta delta, Cancel throw new UnknownStreamKeyException(); } - var messageKeyLease = delta - .GetMessageKey() - .ToLease(streamKey); + if (delta.GetMessageKey() is { } messageKey) + { + var messageKeyLease = messageKey.ToLease(streamKey); - var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); + var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); - if (streamPointer != default) - { - return false; + if (streamPointer != default) + { + return false; + } } var nextStreamPointer = stream.GetNextPointer(); From e24368f7af2dda48fabf39c4d75b9b3fa2502b40 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Sun, 21 Jan 2024 12:14:38 -0800 Subject: [PATCH 91/93] feat: SQS queue processor --- EntityDb.sln | 7 + src/EntityDb.Aws/EntityDb.Aws.csproj | 16 +++ .../Extensions/ServiceCollectionExtensions.cs | 43 ++++++ .../Queues/SqsInboxSourceProcessorQueue.cs | 130 ++++++++++++++++++ .../Queues/SqsOutboxSourceProcessorQueue.cs | 125 +++++++++++++++++ .../Queues/SqsSourceProcessorMessage.cs | 13 ++ .../Queues/SqsSourceProcessorQueueOptions.cs | 46 +++++++ src/EntityDb.Aws/packages.lock.json | 105 ++++++++++++++ .../Properties/AssemblyInfo.cs | 1 + .../Queries/Standard/GetSourceDataQuery.cs | 10 +- .../BufferBlockSourceReprocessorQueue.cs | 2 +- .../TestModeSourceReprocessorQueue.cs | 2 +- .../Sources/SourceRepositoryExtensions.cs | 4 +- .../Projections/OneToOneProjection.cs | 2 +- 14 files changed, 501 insertions(+), 5 deletions(-) create mode 100644 src/EntityDb.Aws/EntityDb.Aws.csproj create mode 100644 src/EntityDb.Aws/Extensions/ServiceCollectionExtensions.cs create mode 100644 src/EntityDb.Aws/Sources/Processors/Queues/SqsInboxSourceProcessorQueue.cs create mode 100644 src/EntityDb.Aws/Sources/Processors/Queues/SqsOutboxSourceProcessorQueue.cs create mode 100644 src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorMessage.cs create mode 100644 src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorQueueOptions.cs create mode 100644 src/EntityDb.Aws/packages.lock.json diff --git a/EntityDb.sln b/EntityDb.sln index 83c05a3c..88b170aa 100644 --- a/EntityDb.sln +++ b/EntityDb.sln @@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Json", "src\Entity EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Provisioner", "src\EntityDb.Provisioner\EntityDb.Provisioner.csproj", "{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityDb.Aws", "src\EntityDb.Aws\EntityDb.Aws.csproj", "{71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -95,6 +97,10 @@ Global {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.Build.0 = Release|Any CPU + {71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -111,6 +117,7 @@ Global {FA2AD2E9-84DA-4667-BF46-140B0B050563} = {92484C44-2754-4C1D-BD46-98D83E4020EE} {4936FFE0-98E5-43A2-89C9-0415A13CAA9B} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} {26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} + {71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E9D288EE-9351-4018-ABE8-B0968AEB0465} diff --git a/src/EntityDb.Aws/EntityDb.Aws.csproj b/src/EntityDb.Aws/EntityDb.Aws.csproj new file mode 100644 index 00000000..a29441d2 --- /dev/null +++ b/src/EntityDb.Aws/EntityDb.Aws.csproj @@ -0,0 +1,16 @@ + + + + EntityDb EventSourcing EventStreaming DDD CQRS + A partial set of implementations of the EntityDb common layer. + + + + + + + + + + + diff --git a/src/EntityDb.Aws/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Aws/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..b711674d --- /dev/null +++ b/src/EntityDb.Aws/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,43 @@ +using EntityDb.Aws.Sources.Processors.Queues; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.Aws.Extensions; + +/// +/// Extensions for service collections. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers a queue for processing sources as they are committed. + /// For test mode, the queue is not actually a queue and will immediately process the source. + /// For non-test mode, the implementation of ISourceProcessorQueue uses a buffer + /// block to receive messages, enqueue them to sqs, and then background-only service + /// will dequeue them from sqs and process them as normal. + /// + /// The service collection. + /// Whether or not to run in test mode. + [ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")] + public static void AddSqsSourceProcessorQueue(this IServiceCollection serviceCollection, + bool testMode) + { + if (testMode) + { + serviceCollection.AddSingleton(); + } + else + { + serviceCollection.AddSingleton(); + + serviceCollection.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); + + serviceCollection.AddHostedService(serviceProvider => + serviceProvider.GetRequiredService()); + + serviceCollection.AddHostedService(); + } + } +} diff --git a/src/EntityDb.Aws/Sources/Processors/Queues/SqsInboxSourceProcessorQueue.cs b/src/EntityDb.Aws/Sources/Processors/Queues/SqsInboxSourceProcessorQueue.cs new file mode 100644 index 00000000..4ff16817 --- /dev/null +++ b/src/EntityDb.Aws/Sources/Processors/Queues/SqsInboxSourceProcessorQueue.cs @@ -0,0 +1,130 @@ +using Amazon.SQS.Model; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Sources; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.TypeResolvers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; + +namespace EntityDb.Aws.Sources.Processors.Queues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal sealed class SqsInboxSourceProcessorQueue : BackgroundService +{ + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly ITypeResolver _typeResolver; + private readonly SqsSourceProcessorQueueOptions _options; + + public SqsInboxSourceProcessorQueue + ( + ILogger logger, + IServiceScopeFactory serviceScopeFactory, + ITypeResolver typeResolver, + IOptionsFactory optionsFactory, + string sqsOptionsName + ) + { + _logger = logger; + _serviceScopeFactory = serviceScopeFactory; + _typeResolver = typeResolver; + _options = optionsFactory.Create(sqsOptionsName); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + const int maxNumberOfMessages = 1; + + var active = true; + + while (!stoppingToken.IsCancellationRequested) + { + await Task.Delay(active ? _options.ActiveDequeueDelay : _options.IdleDequeueDelay, stoppingToken); + + await using var serviceScope = _serviceScopeFactory.CreateAsyncScope(); + + var (amazonSqs, queueUrl) = await _options.AmazonSqsFactory.Invoke(serviceScope.ServiceProvider); + + var receiveMessageResponse = await amazonSqs.ReceiveMessageAsync + ( + new ReceiveMessageRequest + { + QueueUrl = queueUrl, + MaxNumberOfMessages = maxNumberOfMessages, + }, + stoppingToken + ); + + if (receiveMessageResponse.Messages.Count != maxNumberOfMessages) + { + active = false; + continue; + } + + active = true; + + var receivedMessage = receiveMessageResponse.Messages[0]; + + var deleteRequest = new DeleteMessageRequest + { + ReceiptHandle = receivedMessage.ReceiptHandle, + }; + + var sqsSourceProcessorMessage = + JsonSerializer.Deserialize(receivedMessage.Body) ?? + throw new UnreachableException(); + + var sourceId = new Id(sqsSourceProcessorMessage.SourceId); + var stateId = new Id(sqsSourceProcessorMessage.StateId); + + await using var sourceRepositoryFactory = + serviceScope.ServiceProvider.GetRequiredService(); + + await using var sourceRepository = await sourceRepositoryFactory.Create(sqsSourceProcessorMessage.SourceRepositoryOptionsName, + stoppingToken); + + var sourceProcessorTypeName = sqsSourceProcessorMessage.SourceProcessorEnvelopeHeaders.Value[EnvelopeHelper.Type]; + + using var logScope = _logger.BeginScope(new KeyValuePair[] + { + new("QueueUrl", queueUrl), + new("MessageId", receivedMessage.MessageId), + new("ReceiptHandle", receivedMessage.ReceiptHandle), + new("SourceProcessorType", sourceProcessorTypeName), + new("SourceId", sqsSourceProcessorMessage.SourceId), + new("StateId", sqsSourceProcessorMessage.StateId), + }); + + try + { + var source = await sourceRepository.GetSource(sourceId, stateId, stoppingToken); + + var sourceProcessorType = _typeResolver.ResolveType(sqsSourceProcessorMessage.SourceProcessorEnvelopeHeaders); + + var sourceProcessor = + (ISourceProcessor)serviceScope.ServiceProvider.GetRequiredService(sourceProcessorType); + + _logger.Log(_options.DebugLogLevel, "Started processing source"); + + await sourceProcessor.Process(source, stoppingToken); + + _logger.Log(_options.DebugLogLevel, "Finished processing source"); + + await amazonSqs.DeleteMessageAsync(deleteRequest, stoppingToken); + + _logger.Log(LogLevel.Debug, "Message deleted from queue"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Error occurred while processing source"); + } + } + } +} diff --git a/src/EntityDb.Aws/Sources/Processors/Queues/SqsOutboxSourceProcessorQueue.cs b/src/EntityDb.Aws/Sources/Processors/Queues/SqsOutboxSourceProcessorQueue.cs new file mode 100644 index 00000000..0c4e0e8b --- /dev/null +++ b/src/EntityDb.Aws/Sources/Processors/Queues/SqsOutboxSourceProcessorQueue.cs @@ -0,0 +1,125 @@ +using Amazon.SQS.Model; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Threading.Tasks.Dataflow; + +namespace EntityDb.Aws.Sources.Processors.Queues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal sealed class SqsOutboxSourceProcessorQueue : BackgroundService, ISourceProcessorQueue +{ + private readonly BufferBlock _bufferBlock = new(); + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly SqsSourceProcessorQueueOptions _options; + private CancellationTokenSource? _linkedStoppingTokenSource; + + public SqsOutboxSourceProcessorQueue + ( + ILogger logger, + IServiceScopeFactory serviceScopeFactory, + IOptionsFactory optionsFactory, + string optionsName + ) + { + _logger = logger; + _serviceScopeFactory = serviceScopeFactory; + _options = optionsFactory.Create(optionsName); + } + + public void Enqueue(ISourceProcessorQueueItem item) + { + if (_linkedStoppingTokenSource is { IsCancellationRequested: true }) + { + _logger.LogWarning("Application is shutting down when messages are still being received"); + } + + _bufferBlock.Post(item); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + using var linkedStoppingTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); + + _linkedStoppingTokenSource = linkedStoppingTokenSource; + + CancellationToken cancellationToken = default; + + while (await _bufferBlock.OutputAvailableAsync(cancellationToken)) + { + if (_linkedStoppingTokenSource.IsCancellationRequested) + { + _logger.LogWarning("Application is shutting down when messages are still being enqueued"); + } + + await Task.Delay(_options.EnqueueDelay, cancellationToken); + + var item = await _bufferBlock.ReceiveAsync(cancellationToken); + + await using var serviceScope = _serviceScopeFactory.CreateAsyncScope(); + + var (amazonSqs, queueUrl) = await _options.AmazonSqsFactory.Invoke(serviceScope.ServiceProvider); + + using var logScope = _logger.BeginScope(new KeyValuePair[] + { + new("QueueUrl", queueUrl), + new("SourceProcessorType", item.SourceProcessorType.Name), + new("SourceId", item.Source.Id.Value), + }); + + try + { + _logger.Log(_options.DebugLogLevel, "Started enqueueing source"); + + var sourceProcessorEnvelopeHeaders = EnvelopeHelper.GetEnvelopeHeaders(item.SourceProcessorType); + var sourceProcessorTypeName = sourceProcessorEnvelopeHeaders.Value[EnvelopeHelper.Type]; + + var sourceId = item.Source.Id.Value; + + foreach (var message in item.Source.Messages.DistinctBy(message => message.StatePointer.Id)) + { + var stateId = message.StatePointer.Id.Value; + + var sendMessageResponse = await amazonSqs.SendMessageAsync + ( + new SendMessageRequest + { + QueueUrl = queueUrl, + MessageBody = JsonSerializer.Serialize(new SqsSourceProcessorMessage + { + StateId = stateId, + SourceId = sourceId, + SourceRepositoryOptionsName = + _options.GetSourceRepositoryOptionsNameFromDelta(message.Delta), + SourceProcessorEnvelopeHeaders = sourceProcessorEnvelopeHeaders, + }), + MessageGroupId = $"{sourceProcessorTypeName}:{stateId}", + MessageDeduplicationId = $"{sourceId}", + }, + cancellationToken + ); + + _logger.Log(_options.DebugLogLevel, + "Message {MessageId} enqueued for {SourceId} and {StateId} using {SourceProcessorTypeName}", + sendMessageResponse.MessageId, sourceId, stateId, sourceProcessorTypeName); + } + + _logger.Log(_options.DebugLogLevel, "Finished enqueueing source"); + } + catch (Exception exception) + { + _logger.LogError(exception, "Error occurred while enqueueing source"); + } + } + + while (await _bufferBlock.OutputAvailableAsync(cancellationToken)) + { + } + } +} diff --git a/src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorMessage.cs b/src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorMessage.cs new file mode 100644 index 00000000..33d91768 --- /dev/null +++ b/src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorMessage.cs @@ -0,0 +1,13 @@ +using EntityDb.Common.Envelopes; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.Aws.Sources.Processors.Queues; + +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +internal record SqsSourceProcessorMessage +{ + public required Guid StateId { get; init; } + public required Guid SourceId { get; init; } + public required string SourceRepositoryOptionsName { get; init; } + public required EnvelopeHeaders SourceProcessorEnvelopeHeaders { get; init; } +} diff --git a/src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorQueueOptions.cs b/src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorQueueOptions.cs new file mode 100644 index 00000000..d43af4a2 --- /dev/null +++ b/src/EntityDb.Aws/Sources/Processors/Queues/SqsSourceProcessorQueueOptions.cs @@ -0,0 +1,46 @@ +using Amazon.SQS; +using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; + +namespace EntityDb.Aws.Sources.Processors.Queues; + +/// +/// Options for configuring the SQS Source Processor Queues. +/// +[ExcludeFromCodeCoverage(Justification = "Not used in tests.")] +public class SqsSourceProcessorQueueOptions +{ + /// + /// Retrieve the IAmazonSQS client as well as the queue url. Note that queues + /// **MUST** be first-in first-out (FIFO). + /// + public Func> AmazonSqsFactory { get; set; } = _ => throw new NotImplementedException(); + + /// + /// Map the delta to a source repository options name. Should use pattern matching, such as is IReducer{TEntity} + /// or is IMutator{TProjection} + /// + public Func GetSourceRepositoryOptionsNameFromDelta { get; set; } = _ => throw new NotImplementedException(); + + /// + /// Limits how fast sources are enqueued into SQS + /// + public TimeSpan EnqueueDelay { get; set; } = TimeSpan.FromSeconds(5); + + /// + /// Limits how fast sources are dequeued from SQS when the previous attempt to receive a source + /// yielded a source. + /// + public TimeSpan ActiveDequeueDelay { get; set; } = TimeSpan.FromSeconds(10); + + /// + /// Limits how fast sources are dequeued from SQS when the previous attempt to receive a source + /// yielded nothing. + /// + public TimeSpan IdleDequeueDelay { get; set; } = TimeSpan.FromMinutes(1); + + /// + /// Determines the log level of logs that give debugging information. + /// + public LogLevel DebugLogLevel { get; set; } = LogLevel.Debug; +} diff --git a/src/EntityDb.Aws/packages.lock.json b/src/EntityDb.Aws/packages.lock.json new file mode 100644 index 00000000..2662a2cc --- /dev/null +++ b/src/EntityDb.Aws/packages.lock.json @@ -0,0 +1,105 @@ +{ + "version": 1, + "dependencies": { + "net7.0": { + "AWSSDK.SQS": { + "type": "Direct", + "requested": "[3.7.300.39, )", + "resolved": "3.7.300.39", + "contentHash": "1fnbzXCoBQ839BBFf446f3RMHn9dIARqy8zCxxi15GQUrze0/vjAtlhVOP5hbRYPw4Aqak9iEvSnV4cLYjTf9g==", + "dependencies": { + "AWSSDK.Core": "[3.7.301.9, 4.0.0)" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.301.9", + "contentHash": "7Vu9dQSSxaO6l37GyDMS9Jx/GFaqPn3sJfP8j/o9/E6IF+7UfWv1BYAvxNS93J0mnYW/LqnBVJWQH3P7eWtPgw==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } + }, + "net8.0": { + "AWSSDK.SQS": { + "type": "Direct", + "requested": "[3.7.300.39, )", + "resolved": "3.7.300.39", + "contentHash": "1fnbzXCoBQ839BBFf446f3RMHn9dIARqy8zCxxi15GQUrze0/vjAtlhVOP5hbRYPw4Aqak9iEvSnV4cLYjTf9g==", + "dependencies": { + "AWSSDK.Core": "[3.7.301.9, 4.0.0)" + } + }, + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.301.9", + "contentHash": "7Vu9dQSSxaO6l37GyDMS9Jx/GFaqPn3sJfP8j/o9/E6IF+7UfWv1BYAvxNS93J0mnYW/LqnBVJWQH3P7eWtPgw==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "entitydb.abstractions": { + "type": "Project", + "dependencies": { + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.common": { + "type": "Project", + "dependencies": { + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + }, + "entitydb.json": { + "type": "Project", + "dependencies": { + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/EntityDb.Common/Properties/AssemblyInfo.cs b/src/EntityDb.Common/Properties/AssemblyInfo.cs index 9a83aa60..60dcbeb4 100644 --- a/src/EntityDb.Common/Properties/AssemblyInfo.cs +++ b/src/EntityDb.Common/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; // src +[assembly: InternalsVisibleTo("EntityDb.Aws")] [assembly: InternalsVisibleTo("EntityDb.MongoDb")] [assembly: InternalsVisibleTo("EntityDb.Provisioner")] [assembly: InternalsVisibleTo("EntityDb.Mvc")] diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs index 18405fae..34990e54 100644 --- a/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs @@ -5,10 +5,18 @@ namespace EntityDb.Common.Sources.Queries.Standard; -internal sealed record GetSourceDataQuery(Id SourceId) : ISourceDataQuery, IMessageDataQuery +internal sealed record GetSourceDataQuery(Id SourceId, Id? StateId) : ISourceDataQuery, IMessageDataQuery { public TFilter GetFilter(IMessageDataFilterBuilder builder) { + if (StateId.HasValue) + { + return builder.And + ( + builder.StateIdIn(StateId.Value), + builder.SourceIdIn(SourceId) + ); + } return builder.SourceIdIn(SourceId); } diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs index b6dde1d1..0649b60b 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs @@ -57,7 +57,7 @@ await _sourceRepositoryFactory.Create(item.SourceSessionOptionsName, await Task.Delay(item.EnqueueDelay, cancellationToken); var source = await sourceRepository - .GetSource(sourceId, cancellationToken); + .GetSource(sourceId, default, cancellationToken); _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem { diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs index 8f15f7cb..a305f8c0 100644 --- a/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs @@ -40,7 +40,7 @@ await _sourceRepositoryFactory.Create(item.SourceSessionOptionsName, foreach (var sourceId in sourceIds) { var source = await sourceRepository - .GetSource(sourceId, cancellationToken); + .GetSource(sourceId, default, cancellationToken); _sourceProcessorQueue.Enqueue(new SourceProcessorQueueItem { diff --git a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs index a6b052b0..ec5f8429 100644 --- a/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs +++ b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs @@ -36,6 +36,7 @@ public static IAsyncEnumerable EnumerateSourceIds(this ISourceRepository sou /// /// The source repository /// The source id + /// Optional: The state id /// A cancellation token /// An instance of . /// @@ -60,10 +61,11 @@ public static async Task GetSource ( this ISourceRepository sourceRepository, Id sourceId, + Id? stateId = default, CancellationToken cancellationToken = default ) { - var query = new GetSourceDataQuery(sourceId); + var query = new GetSourceDataQuery(sourceId, stateId); var annotatedAgentSignature = await sourceRepository .EnumerateAnnotatedAgentSignatures(query, cancellationToken) diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index f51135e1..76f00fb3 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -75,7 +75,7 @@ [EnumeratorCancellation] CancellationToken cancellationToken foreach (var sourceId in sourceIds) { yield return await sourceRepository - .GetSource(sourceId, cancellationToken); + .GetSource(sourceId, default, cancellationToken); } } From ba4a93f9c785dc36a841f8aad412e0320dec37c8 Mon Sep 17 00:00:00 2001 From: chefnote-com-admin Date: Sun, 11 Aug 2024 18:30:17 -0700 Subject: [PATCH 92/93] feat: ability to update assembly names in default partial type resolver --- .../TypeResolvers/DefaultPartialTypeResolver.cs | 5 +++++ .../TypeResolvers/DefaultPartialTypeResolverOptions.cs | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs index 20f8dd9e..df1ad086 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs @@ -38,6 +38,11 @@ public bool TryResolveType(EnvelopeHeaders envelopeHeaders, [NotNullWhen(true)] private Assembly AssemblyResolver(AssemblyName assemblyName) { + if (assemblyName.Name is { } originalName && _options.Value.UpdateNames.TryGetValue(originalName, out var newName)) + { + assemblyName.Name = newName; + } + if (_options.Value.IgnoreVersion) { assemblyName.Version = null; diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs index 8ffa0bdb..8bbf8442 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs @@ -5,6 +5,13 @@ namespace EntityDb.Common.TypeResolvers; /// public sealed class DefaultPartialTypeResolverOptions { + /// + /// If you rename your assemblies, you will want to update the + /// name of the assembly for type resolving purposes, at least + /// until you update the data header. + /// + public Dictionary UpdateNames { get; set; } = new(); + /// /// If you version your assemblies, you may want to ignore the /// version of the assembly for type resolving purposes. From 7eef2e2f5611e6a6783b81bcb3383b7d50624aec Mon Sep 17 00:00:00 2001 From: chefnote-com-admin Date: Sun, 11 Aug 2024 20:34:11 -0700 Subject: [PATCH 93/93] refactor: update _namespaces_, and cache so that we don't have to repeat the lookup logic over and over --- .../DefaultPartialTypeResolver.cs | 52 +++++++++++++++---- .../DefaultPartialTypeResolverOptions.cs | 12 ++++- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs index df1ad086..e6a44905 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs @@ -8,6 +8,7 @@ namespace EntityDb.Common.TypeResolvers; internal sealed class DefaultPartialTypeResolver : IPartialTypeResolver { private readonly IOptions _options; + private readonly Dictionary _cache = new(); public DefaultPartialTypeResolver(IOptions options) { @@ -24,25 +25,44 @@ public bool TryResolveType(EnvelopeHeaders envelopeHeaders, [NotNullWhen(true)] return false; } - resolvedType = Type.GetType - ( - Assembly.CreateQualifiedName(assemblyFullName, typeFullName), - AssemblyResolver, - (assembly, typeName, ignoreCase) => assembly!.GetType(typeName, true, ignoreCase), - true, - false - )!; + var qualifiedTypeName = Assembly.CreateQualifiedName(assemblyFullName, typeFullName); + + if (_cache.TryGetValue(qualifiedTypeName, out resolvedType)) + { + return true; + } + + if (!TryGetType(qualifiedTypeName, out resolvedType)) + { + return false; + } + + _cache.Add(qualifiedTypeName, resolvedType); return true; } - private Assembly AssemblyResolver(AssemblyName assemblyName) + private bool TryGetType(string qualifiedTypeName, [NotNullWhen(true)] out Type? resolvedType) { - if (assemblyName.Name is { } originalName && _options.Value.UpdateNames.TryGetValue(originalName, out var newName)) + foreach (var (oldNamespace, newNamespace) in _options.Value.UpdateNamespaces) { - assemblyName.Name = newName; + qualifiedTypeName = qualifiedTypeName.Replace(oldNamespace, newNamespace); } + resolvedType = Type.GetType + ( + qualifiedTypeName, + AssemblyResolver, + TypeResolver, + _options.Value.ThrowOnError, + _options.Value.IgnoreCase + ); + + return resolvedType is not null; + } + + private Assembly AssemblyResolver(AssemblyName assemblyName) + { if (_options.Value.IgnoreVersion) { assemblyName.Version = null; @@ -50,4 +70,14 @@ private Assembly AssemblyResolver(AssemblyName assemblyName) return Assembly.Load(assemblyName); } + + private Type? TypeResolver(Assembly? assembly, string typeName, bool ignoreCase) + { + return assembly?.GetType + ( + typeName, + _options.Value.ThrowOnError, + ignoreCase + ); + } } diff --git a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs index 8bbf8442..80ba20a1 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs @@ -5,12 +5,22 @@ namespace EntityDb.Common.TypeResolvers; /// public sealed class DefaultPartialTypeResolverOptions { + /// + /// Throw an exception if the type cannot be resolved. + /// + public bool ThrowOnError { get; set; } = true; + + /// + /// Perform a case-insensitive search for the type name. + /// + public bool IgnoreCase { get; set; } = false; + /// /// If you rename your assemblies, you will want to update the /// name of the assembly for type resolving purposes, at least /// until you update the data header. /// - public Dictionary UpdateNames { get; set; } = new(); + public Dictionary UpdateNamespaces { get; set; } = new(); /// /// If you version your assemblies, you may want to ignore the