diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 944d50c0..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/src/.editorconfig b/.editorconfig similarity index 100% rename from src/.editorconfig rename to .editorconfig diff --git a/Directory.Build.props b/Directory.Build.props index 0eef874e..7f02f44a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,11 +1,12 @@ - net7.0 + net7.0;net8.0 enable - enable - + enable + 12 + - - - + + + \ No newline at end of file diff --git a/EntityDb.sln b/EntityDb.sln index 3f73be88..88b170aa 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 @@ -37,19 +35,17 @@ 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.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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityDb.Aws", "src\EntityDb.Aws\EntityDb.Aws.csproj", "{71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -77,10 +73,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 +89,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 +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 - {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 + {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 @@ -131,17 +111,13 @@ 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} + {71EF2C8C-F6BF-4CE3-91A4-62F8C7C9FB8B} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E9D288EE-9351-4018-ABE8-B0968AEB0465} diff --git a/LICENSE.txt b/LICENSE.txt index f190c3c5..6df370cd 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 - 2022 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 1f84db91..f2d5b2e5 100644 --- a/README.md +++ b/README.md @@ -25,86 +25,107 @@ 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. +There are several core objects at the heart of this implementation. Encapsulating these objects are various +repositories. -1. Transaction Repository - - Agents - - Commands - - Tags - - Leases -2. Snapshot Repository - - Snapshots -2. Entity Repository - - Transaction Repository - - Optional: Snapshot Repository -3. Projection Repository - - Transaction Repository - - Optional: Snapshot Repository +1. Source Repository + - Agent Signatures + - Deltas + - Tags + - Leases +2. State Repository + - States +3. Stream Repository +4. Entity Repository +5. Projection Repository -### Transactions +### Sources -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 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 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 and interact with states. Whenever a source is committed, +the signature of the agent is recorded with it. -### Commands +For example, if a source is initiated via an HTTP API, you +might use the `HttpContextAgent` - it's signature includes headers and connection information. -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. +### Deltas + +A delta represents a change in state. Literally anything can be a delta. + +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 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 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 -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 +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, 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 number. -Together, the identifier and version number 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 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. +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 root 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 root, 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. - -### Communications - -- Coming Soon!TM 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 ceb31304..dc106cda 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.201", + "version": "8.0.100", "allowPrerelease": false, "rollForward": "disable" } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 1fda23bd..e1cf0076 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 - 2024 © entitydb.io https://github.com/entitydb-io/entitydb git en 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/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/IEntity.cs b/src/EntityDb.Abstractions/Entities/IEntity.cs new file mode 100644 index 00000000..4811829c --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/IEntity.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Indicates the entity is compatible with several EntityDb.Common implementations. +/// +/// The type of the entity. +public interface IEntity : IState +{ + /// + /// 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 deleted file mode 100644 index 19dd0fae..00000000 --- a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs +++ /dev/null @@ -1,39 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Entities; - -/// -/// Encapsulates the transaction repository and the snapshot repository of an entity. -/// -/// The type of the entity. -public interface IEntityRepository : IDisposableResource -{ - /// - /// The backing transaction repository. - /// - ITransactionRepository TransactionRepository { get; } - - /// - /// The backing snapshot repository (if snapshot is available). - /// - ISnapshotRepository? SnapshotRepository { get; } - - /// - /// Returns the snapshot of a for a given . - /// - /// A pointer to the entity. - /// A cancellation token. - /// The snapshot of a for . - Task GetSnapshot(Pointer entityPointer, 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/Entities/IEntityRepositoryFactory.cs b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs index 975863b1..34477f4b 100644 --- a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs +++ b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs @@ -1,18 +1,65 @@ +using EntityDb.Abstractions.States; + 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 state repository. + /// A cancellation token. + /// A new instance of . + Task> CreateSingleForNew + ( + Id entityId, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default + ); + + /// + /// Create a new instance of + /// for an existing entity. + /// + /// 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. + /// A cancellation token. + /// A new instance of . + Task> CreateSingleForExisting + ( + StatePointer entityPointer, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default + ); + + /// + /// Create a new instance of /// - /// The agent's use case for the transaction repository. - /// The agent's use case for the snapshot repository. + /// 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. /// A cancellation token. - /// A new instance of . - Task> CreateRepository(string transactionSessionOptionsName, - string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default); + /// A new instance of . + Task> CreateMultiple + ( + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? stateSessionOptionsName = null, + 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..f03f6242 --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/IMultipleEntityRepository.cs @@ -0,0 +1,64 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Manages the sources and states of multiple entities. +/// +/// The type of the entity. +public interface IMultipleEntityRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// The backing state repository (if state is available). + /// + IStateRepository? StateRepository { get; } + + /// + /// Start a new entity with 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 state id. + /// + /// A state pointer associated with a . + /// A cancellation token + /// A task. + /// + /// Call this method to load an entity that already exists before calling + /// . + /// + Task TryLoad(StatePointer entityPointer, CancellationToken cancellationToken = default); + + /// + /// Returns the state of a for a given . + /// + /// The id of the entity. + /// The state of a for . + TEntity Get(Id entityId); + + /// + /// 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 cancellation token. + /// 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 new file mode 100644 index 00000000..5bcf362d --- /dev/null +++ b/src/EntityDb.Abstractions/Entities/ISingleEntityRepository.cs @@ -0,0 +1,46 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Entities; + +/// +/// Manages the sources and states of a single entity. +/// +/// The type of the entity. +public interface ISingleEntityRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// The backing state repository (if state is available). + /// + IStateRepository? StateRepository { get; } + + /// + /// The state pointer for the current entity. + /// + StatePointer EntityPointer { get; } + + /// + /// Returns the state of a . + /// + /// The state 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. + /// + /// A cancellation token. + /// Only returns false if there are uncommitted messages. + Task Commit(CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj b/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj index 9e371f45..96e7fa11 100644 --- a/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj +++ b/src/EntityDb.Abstractions/EntityDb.Abstractions.csproj @@ -1,9 +1,9 @@  - - EntityDb EventSourcing DDD CQRS + EntityDb EventSourcing EventStreaming DDD CQRS An abstraction layer for the EntityDb suite of packages. + 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 new file mode 100644 index 00000000..dde3525b --- /dev/null +++ b/src/EntityDb.Abstractions/Projections/IProjection.cs @@ -0,0 +1,39 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Projections; + +/// +/// Provides basic functionality for the common implementation of projections. +/// +/// +public interface IProjection : IState +{ + /// + /// 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. + /// The state pointer for the desired projection. + /// A cancellation token + /// + /// A that is used to load the rest of the messages + /// for the given state pointer. + /// + IAsyncEnumerable EnumerateSources(IServiceProvider serviceProvider, StatePointer projectionPointer, + CancellationToken cancellationToken); + + /// + /// 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 state ids for the projections. + static abstract IEnumerable EnumerateProjectionIds(Source source); +} diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs index 42ed6980..f898aac8 100644 --- a/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs +++ b/src/EntityDb.Abstractions/Projections/IProjectionRepository.cs @@ -1,38 +1,26 @@ using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; 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 transaction repository. + /// The backing state repository. /// - ITransactionRepository TransactionRepository { get; } + IStateRepository? StateRepository { get; } /// - /// The backing snapshot repository. + /// Returns the state of a for a given . /// - ISnapshotRepository? SnapshotRepository { get; } - - /// - /// Returns the snapshot of a for a given . - /// - /// A pointer to the projection. + /// The state 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 TryLoad(StatePointer projectionPointer, CancellationToken cancellationToken = default); - /// - /// Maps an entity to a projection id, or default if the entity does not map to this projection. - /// - /// The entity object. - /// The projection id for the entity, or default if none. - Id? GetProjectionIdOrDefault(object entity); + TProjection Get(Id projectionId); } diff --git a/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs b/src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs index a12c6502..5f1c6147 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. + /// The agent's use case for the state repository. /// A cancellation token. /// A new instance of . - Task> CreateRepository(string transactionSessionOptionsName, - string snapshotSessionOptionsName, CancellationToken cancellationToken = default); + Task> CreateRepository(string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default); } 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/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs deleted file mode 100644 index 7744a93b..00000000 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/IAgentSignatureFilterBuilder.cs +++ /dev/null @@ -1,34 +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 -{ - /// - /// Returns a that only includes agentSignatures with any entity id which is contained - /// in a set - /// of entity ids. - /// - /// The set of entity ids. - /// - /// A that only includes agentSignatures with any entity id which is contained in - /// . - /// - TFilter EntityIdsIn(params Id[] entityIds); - - /// - /// 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/IFilterBuilder.cs b/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs deleted file mode 100644 index aa797265..00000000 --- a/src/EntityDb.Abstractions/Queries/FilterBuilders/IFilterBuilder.cs +++ /dev/null @@ -1,72 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Queries.FilterBuilders; - -/// -/// Builds a for an object repository. Possible objects include: agentSignatures, -/// commands, -/// facts, and leases. -/// -/// The type of filter used by the repository. -public interface IFilterBuilder -{ - /// - /// Returns a that only includes objects with a transaction timestamp greater than or - /// equal to a transaction timestamp. - /// - /// The transaction timestamp. - /// - /// A that only includes objects with an transaction timestamp greater than or - /// equal to . - /// - TFilter TransactionTimeStampGte(TimeStamp transactionTimeStamp); - - /// - /// Returns a that only includes objects with a transaction timestamp less than or - /// equal to a transaction timestamp. - /// - /// The transaction timestamp. - /// - /// A that only includes objects with an transaction timestamp less than or equal - /// to . - /// - TFilter TransactionTimeStampLte(TimeStamp transactionTimeStamp); - - /// - /// Returns a that only includes objects with an transaction id which is contained in a - /// set of transaction ids. - /// - /// The set of transaction ids. - /// - /// A that only includes objects with an transaction id which is contained in - /// . - /// - TFilter TransactionIdIn(params Id[] transactionIds); - - /// - /// Returns a that excludes objects which do match a filter. - /// - /// The filter. - /// 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. - /// - /// The set of filters. - /// - /// A that excludes objects which do not match all filters in - /// . - /// - TFilter And(params TFilter[] 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 - /// . - /// - TFilter Or(params TFilter[] filters); -} 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/Snapshots/ISnapshotRepository.cs b/src/EntityDb.Abstractions/Snapshots/ISnapshotRepository.cs deleted file mode 100644 index 31d4d68a..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 specific 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/Agents/IAgent.cs b/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs similarity index 66% rename from src/EntityDb.Abstractions/Agents/IAgent.cs rename to src/EntityDb.Abstractions/Sources/Agents/IAgent.cs index aa3a2dec..a7316136 100644 --- a/src/EntityDb.Abstractions/Agents/IAgent.cs +++ b/src/EntityDb.Abstractions/Sources/Agents/IAgent.cs @@ -1,9 +1,7 @@ -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/IAnnotatedMessageData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs new file mode 100644 index 00000000..dce12cfc --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedMessageData.cs @@ -0,0 +1,35 @@ +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Sources.Annotations; + +/// +/// Annotated message-level data +/// +/// The type of the data +public interface IAnnotatedMessageData +{ + /// + /// The id of the source + /// + Id SourceId { get; } + + /// + /// The time stamp of the source + /// + TimeStamp SourceTimeStamp { get; } + + /// + /// The id of the message + /// + Id MessageId { get; } + + /// + /// The state pointer + /// + StatePointer StatePointer { get; } + + /// + /// The data + /// + TData Data { get; } +} diff --git a/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs new file mode 100644 index 00000000..9dac7bbd --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Annotations/IAnnotatedSourceData.cs @@ -0,0 +1,35 @@ +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Sources.Annotations; + +/// +/// Annotated source-level 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 message ids + /// + Id[] MessageIds { get; } + + /// + /// The state pointers + /// + StatePointer[] StatePointers { get; } + + /// + /// The data + /// + TData Data { get; } +} diff --git a/src/EntityDb.Abstractions/Leases/ILease.cs b/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs similarity index 73% rename from src/EntityDb.Abstractions/Leases/ILease.cs rename to src/EntityDb.Abstractions/Sources/Attributes/ILease.cs index ae71ac5a..dca52959 100644 --- a/src/EntityDb.Abstractions/Leases/ILease.cs +++ b/src/EntityDb.Abstractions/Sources/Attributes/ILease.cs @@ -1,14 +1,14 @@ -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 -/// 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/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/Tags/ITag.cs b/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs similarity index 63% rename from src/EntityDb.Abstractions/Tags/ITag.cs rename to src/EntityDb.Abstractions/Sources/Attributes/ITag.cs index 4638f865..22c11875 100644 --- a/src/EntityDb.Abstractions/Tags/ITag.cs +++ b/src/EntityDb.Abstractions/Sources/Attributes/ITag.cs @@ -1,7 +1,7 @@ -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. +/// Represents a single metadata property. /// public interface ITag { diff --git a/src/EntityDb.Abstractions/Sources/ISourceRepository.cs b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs new file mode 100644 index 00000000..f5f1f8fa --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/ISourceRepository.cs @@ -0,0 +1,148 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; + +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 source data query. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// The source ids which are found by . + IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default); + + /// + /// 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, + CancellationToken cancellationToken = default); + + /// + /// Returns the state pointers which are found by a message query. + /// + /// The message data query. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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. + /// A cancellation token. + /// 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 data query. + /// A cancellation token. + /// The annotated deltas which are found by . + IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default); + + /// + /// Atomically commits a single source. + /// + /// The source. + /// A cancellation token. + /// Returns true unless the source cannot be committed. + 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..fa8b1cd7 --- /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 Create(string sourceSessionOptionsName, + CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs b/src/EntityDb.Abstractions/Sources/ISourceSubscriber.cs new file mode 100644 index 00000000..d1092ac5 --- /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(Source source); +} diff --git a/src/EntityDb.Abstractions/Sources/Message.cs b/src/EntityDb.Abstractions/Sources/Message.cs new file mode 100644 index 00000000..23978b49 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Message.cs @@ -0,0 +1,101 @@ +using EntityDb.Abstractions.Extensions; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Abstractions.Sources; + +/// +/// A message that belongs to a single source +/// +public sealed record Message +{ + /// + /// The id assigned to the message. + /// + public required Id Id { get; init; } + + /// + /// The state pointer + /// + public required StatePointer StatePointer { get; init; } + + /// + /// The data. + /// + public required object Delta { get; init; } + + /// + /// The leases to be added. + /// + public ILease[] AddLeases { get; init; } = Array.Empty(); + + /// + /// The tags to be added. + /// + public ITag[] AddTags { get; init; } = Array.Empty(); + + /// + /// The leases to be deleted. + /// + public ILease[] DeleteLeases { get; init; } = Array.Empty(); + + /// + /// The tags to be deleted. + /// + public ITag[] DeleteTags { get; init; } = Array.Empty(); + + internal static Message NewMessage + ( + TState state, + StatePointer statePointer, + object delta, + IStateKey? stateKey = default + ) + { + 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 && addMessageKeyDelta.GetMessageKey() is { } messageKey) + { + addLeases = addLeases + .AppendOrStart(messageKey.ToLease(stateKey)); + } + } + else if (delta is IAddLeasesDelta addLeasesDelta) + { + addLeases = addLeasesDelta.GetLeases(state); + } + + return new Message + { + Id = Id.NewId(), + StatePointer = statePointer, + Delta = delta, + AddLeases = addLeases?.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/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs new file mode 100644 index 00000000..aad50b61 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IDataFilterBuilder.cs @@ -0,0 +1,85 @@ +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; + +/// +/// Builds a for an object. Possible objects include: +/// agent signatures, deltas, facts, and tags. +/// +/// The type of filter used by the repository. +public interface IDataFilterBuilder +{ + /// + /// 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 . + /// + TFilter SourceTimeStampGte(TimeStamp sourceTimeStamp); + + /// + /// 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 . + /// + TFilter SourceTimeStampLte(TimeStamp sourceTimeStamp); + + /// + /// 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 . + /// + 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 + /// . + /// + TFilter Not(TFilter filter); + + /// + /// Returns a that excludes objects which do not match + /// all filters in a set of filters. + /// + /// The set of filters. + /// + /// A that excludes objects which do not match all filters in + /// . + /// + TFilter And(params TFilter[] 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 . + /// + TFilter Or(params TFilter[] filters); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.cs new file mode 100644 index 00000000..1b8dbe08 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ILeaseDataFilterBuilder.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 ILeaseDataFilterBuilder : IMessageDataFilterBuilder +{ + /// + /// 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/IMessageDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs new file mode 100644 index 00000000..9a2de5fd --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/IMessageDataFilterBuilder.cs @@ -0,0 +1,45 @@ +using EntityDb.Abstractions.States; + +namespace EntityDb.Abstractions.Sources.Queries.FilterBuilders; + +/// +/// Builds a for an object associated +/// with a single message. Possible objects include: +/// deltas, facts, and tags. +/// +/// The type of filter used by the repository. +public interface IMessageDataFilterBuilder : IDataFilterBuilder +{ + /// + /// Returns a that only includes objects with a state + /// id in a set of state ids. + /// + /// The state ids. + /// + /// Returns a that only includes objects with a state + /// id in . + /// + TFilter StateIdIn(params Id[] stateIds); + + /// + /// Returns a that only includes objects with an state + /// version greater than or equal to a specific state version. + /// + /// The state ids. + /// + /// Returns a that only includes objects with an state + /// version greater than or equal to . + /// + 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. + /// + /// Returns a that only includes objects with an state + /// version less than or equal to . + /// + TFilter StateVersionLte(StateVersion stateStateVersion); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs new file mode 100644 index 00000000..7b027715 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ISourceDataFilterBuilder.cs @@ -0,0 +1,21 @@ +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 ISourceDataFilterBuilder : IDataFilterBuilder +{ + /// + /// Returns a that only includes objects with any state + /// id in a set of state ids. + /// + /// The state ids. + /// + /// Returns a that only includes objects with any state + /// id in . + /// + TFilter AnyStateIdIn(params Id[] stateIds); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.cs new file mode 100644 index 00000000..f202b4e8 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/FilterBuilders/ITagDataFilterBuilder.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 ITagDataFilterBuilder : IMessageDataFilterBuilder +{ + /// + /// 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/IQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IDataQuery.cs similarity index 71% rename from src/EntityDb.Abstractions/Queries/IQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IDataQuery.cs index 5b85d6ac..9fa5fb9e 100644 --- a/src/EntityDb.Abstractions/Queries/IQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IDataQuery.cs @@ -1,9 +1,10 @@ -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, and leases. /// -public interface IQuery +public interface IDataQuery { /// /// The number of objects to skip. diff --git a/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataQuery.cs similarity index 68% rename from src/EntityDb.Abstractions/Queries/ILeaseQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ILeaseDataQuery.cs index c6ce4c7e..f4662378 100644 --- a/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ILeaseDataQuery.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 leases. /// -public interface ILeaseQuery : IQuery +public interface ILeaseDataQuery : IDataQuery { /// /// Returns a built from a lease filter builder. @@ -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. @@ -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/Queries/ICommandQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs similarity index 50% rename from src/EntityDb.Abstractions/Queries/ICommandQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.cs index 290c6527..26be82a4 100644 --- a/src/EntityDb.Abstractions/Queries/ICommandQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/IMessageDataQuery.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 messages. /// -public interface ICommandQuery : IQuery +public interface IMessageDataQuery : IDataQuery { /// - /// 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(IMessageDataFilterBuilder 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(IMessageDataSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Queries/IAgentSignatureQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs similarity index 69% rename from src/EntityDb.Abstractions/Queries/IAgentSignatureQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.cs index 1478abc4..f9b844bc 100644 --- a/src/EntityDb.Abstractions/Queries/IAgentSignatureQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ISourceDataQuery.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 ISourceDataQuery : IDataQuery { /// /// 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(ISourceDataFilterBuilder 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(ISourceDataSortBuilder builder); } diff --git a/src/EntityDb.Abstractions/Queries/ITagQuery.cs b/src/EntityDb.Abstractions/Sources/Queries/ITagDataQuery.cs similarity index 68% rename from src/EntityDb.Abstractions/Queries/ITagQuery.cs rename to src/EntityDb.Abstractions/Sources/Queries/ITagDataQuery.cs index af5694c1..4329c85c 100644 --- a/src/EntityDb.Abstractions/Queries/ITagQuery.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/ITagDataQuery.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 tags. /// -public interface ITagQuery : IQuery +public interface ITagDataQuery : IDataQuery { /// /// Returns a built from a tag filter builder. @@ -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. @@ -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/Queries/SortBuilders/ISortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IDataSortBuilder.cs similarity index 58% rename from src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs rename to src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IDataSortBuilder.cs index 2fef6aa3..1c084f49 100644 --- a/src/EntityDb.Abstractions/Queries/SortBuilders/ISortBuilder.cs +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IDataSortBuilder.cs @@ -1,24 +1,32 @@ -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, and leases. /// /// The type of sort used by the repository. -public interface ISortBuilder +public interface IDataSortBuilder { /// - /// 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 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/ILeaseDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.cs new file mode 100644 index 00000000..2e3c7c97 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ILeaseDataSortBuilder.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 ILeaseDataSortBuilder : IMessageDataSortBuilder +{ + /// + /// 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/IMessageDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs new file mode 100644 index 00000000..ebee5d7a --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/IMessageDataSortBuilder.cs @@ -0,0 +1,22 @@ +namespace EntityDb.Abstractions.Sources.Queries.SortBuilders; + +/// +/// Builds a for a message query. +/// +/// The type of sort used by the repository. +public interface IMessageDataSortBuilder : IDataSortBuilder +{ + /// + /// Returns a that orders objects by state id. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by state id. + TSort StateId(bool ascending); + + /// + /// Returns a that orders objects by state version. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by state version. + TSort StateVersion(bool ascending); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.cs new file mode 100644 index 00000000..87f45db9 --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ISourceDataSortBuilder.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 ISourceDataSortBuilder : IDataSortBuilder +{ + /// + /// Returns a that orders objects by state ids. + /// + /// Pass true for ascending order or false for descending order. + /// A that orders objects by state ids. + TSort StateIds(bool ascending); +} diff --git a/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.cs new file mode 100644 index 00000000..2cef1b0c --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Queries/SortBuilders/ITagDataSortBuilder.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 ITagDataSortBuilder : IMessageDataSortBuilder +{ + /// + /// 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..0ebfcabe --- /dev/null +++ b/src/EntityDb.Abstractions/Sources/Source.cs @@ -0,0 +1,27 @@ +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 Message[] Messages { get; init; } +} 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 new file mode 100644 index 00000000..7b76c214 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IAddLeasesDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.Sources.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/IAddMessageKeyDelta.cs b/src/EntityDb.Abstractions/States/Deltas/IAddMessageKeyDelta.cs new file mode 100644 index 00000000..b14671d0 --- /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 new file mode 100644 index 00000000..160fadd2 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IAddTagsDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.Sources.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..842b2753 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IDeleteLeasesDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.Sources.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..5ac5b5a6 --- /dev/null +++ b/src/EntityDb.Abstractions/States/Deltas/IDeleteTagsDelta.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.Sources.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..cba8e19a --- /dev/null +++ b/src/EntityDb.Abstractions/States/IState.cs @@ -0,0 +1,37 @@ +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 state pointer. + /// A new instance of . + static abstract TState Construct(StatePointer statePointer); + + /// + /// Returns the state pointer for the current state. + /// + /// The state pointer for the current state. + StatePointer 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 ShouldPersist() => false; + + /// + /// Indicates if this state instance should be recorded as the latest state. + /// + /// true if this state instance should be recorded as the latest state, or else false. + bool ShouldPersistAsLatest() => false; +} diff --git a/src/EntityDb.Abstractions/States/IStateRepository.cs b/src/EntityDb.Abstractions/States/IStateRepository.cs new file mode 100644 index 00000000..86fa1ba5 --- /dev/null +++ b/src/EntityDb.Abstractions/States/IStateRepository.cs @@ -0,0 +1,39 @@ +using EntityDb.Abstractions.Disposables; + +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(). + /// + /// The state pointer. + /// A cancellation token. + /// + /// An exact version of state of a or + /// default(). + /// + Task Get(StatePointer statePointer, CancellationToken cancellationToken = default); + + /// + /// Inserts a state. + /// + /// The state pointer. + /// The state. + /// A cancellation token. + /// true if the insert succeeded, or false if the insert failed. + Task Put(StatePointer statePointer, TState state, CancellationToken cancellationToken = default); + + /// + /// Deletes multiple states. + /// + /// The state pointers to delete. + /// A cancellation token. + /// true if the deletes all succeeded, or false if any deletes failed. + Task Delete(StatePointer[] 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/States/StatePointer.cs b/src/EntityDb.Abstractions/States/StatePointer.cs new file mode 100644 index 00000000..d7fa3e52 --- /dev/null +++ b/src/EntityDb.Abstractions/States/StatePointer.cs @@ -0,0 +1,56 @@ +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}"; + } + + /// + /// 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. + /// + /// 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..c1bf36cd --- /dev/null +++ b/src/EntityDb.Abstractions/States/StateVersion.cs @@ -0,0 +1,44 @@ +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 Previous() + { + return new StateVersion(Value - 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/States/Transforms/IMutator.cs b/src/EntityDb.Abstractions/States/Transforms/IMutator.cs new file mode 100644 index 00000000..abb790ca --- /dev/null +++ b/src/EntityDb.Abstractions/States/Transforms/IMutator.cs @@ -0,0 +1,14 @@ +namespace EntityDb.Abstractions.States.Transforms; + +/// +/// 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/Reducers/IReducer.cs b/src/EntityDb.Abstractions/States/Transforms/IReducer.cs similarity index 83% rename from src/EntityDb.Abstractions/Reducers/IReducer.cs rename to src/EntityDb.Abstractions/States/Transforms/IReducer.cs index d46b1df9..0656ec9d 100644 --- a/src/EntityDb.Abstractions/Reducers/IReducer.cs +++ b/src/EntityDb.Abstractions/States/Transforms/IReducer.cs @@ -1,16 +1,16 @@ -namespace EntityDb.Abstractions.Reducers; - -/// -/// 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. - /// - TState Reduce(TState state); -} +namespace EntityDb.Abstractions.States.Transforms; + +/// +/// 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.Abstractions/Streams/IMultipleStreamRepository.cs b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs new file mode 100644 index 00000000..ddcf0244 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/IMultipleStreamRepository.cs @@ -0,0 +1,51 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Abstractions.Streams; + +/// +/// Encapsulates the source repository and manages +/// +public interface IMultipleStreamRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + 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(IStateKey streamKey, CancellationToken cancellationToken = default); + + void Append(IStateKey streamKey, object delta); + + /// + /// Stages a single delta with a given state key. + /// + /// The key associated with the stream. + /// The new delta that modifies the stream. + /// A cancellation token. + /// + /// Only false if + /// returns a message key that already exists. + /// + Task Append(IStateKey streamKey, TDelta delta, CancellationToken cancellationToken = default) + where TDelta : IAddMessageKeyDelta; + + /// + /// Commits all stages deltas. + /// + /// A cancellation token + /// 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 new file mode 100644 index 00000000..098b7a75 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/ISingleStreamRepository.cs @@ -0,0 +1,44 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Abstractions.Streams; + +/// +/// Encapsulates the source repository and manages +/// +public interface ISingleStreamRepository : IDisposableResource +{ + /// + /// The backing source repository. + /// + ISourceRepository SourceRepository { get; } + + /// + /// The state pointer for the current stream. + /// + IStateKey StreamKey { get; } + + void Append(object delta); + + /// + /// Stages a single delta with a given state key and message key. + /// + /// 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. + /// + Task Append(TDelta delta, CancellationToken cancellationToken = default) + where TDelta : IAddMessageKeyDelta; + + /// + /// Commits all stages deltas. + /// + /// A cancellation token + /// Only returns false if there are uncommitted messages. + Task Commit(CancellationToken cancellationToken = default); +} diff --git a/src/EntityDb.Abstractions/Streams/IStream.cs b/src/EntityDb.Abstractions/Streams/IStream.cs new file mode 100644 index 00000000..5a6a4d35 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/IStream.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Abstractions.Streams; + +public interface IStream +{ + IStateKey Key { get; } + Id Id { get; } +} diff --git a/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs new file mode 100644 index 00000000..5236c0f2 --- /dev/null +++ b/src/EntityDb.Abstractions/Streams/IStreamRepositoryFactory.cs @@ -0,0 +1,53 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources.Attributes; + +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 + ( + IStateKey streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ); + + Task CreateSingleForNew + ( + IStateKey streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ); + + Task CreateSingleForExisting + ( + IStateKey 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/Transactions/Builders/ISingleEntityTransactionBuilder.cs b/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs deleted file mode 100644 index 033348fa..00000000 --- a/src/EntityDb.Abstractions/Transactions/Builders/ISingleEntityTransactionBuilder.cs +++ /dev/null @@ -1,82 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Abstractions.Transactions.Builders; - -/// -/// A transaction builder for a single entity. -/// -/// The type of the entity. -public interface ISingleEntityTransactionBuilder -{ - /// - /// The id used for all transaction 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 transaction builder. - /// - /// Call this method to load an entity that already exists before calling - /// . - /// - ISingleEntityTransactionBuilder Load(TEntity entity); - - /// - /// Adds a transaction step that appends a single command. - /// - /// 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 . - /// - /// A new id for the new transaction. - /// A new instance of . - ITransaction Build(Id transactionId); -} diff --git a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs b/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs deleted file mode 100644 index db6a8182..00000000 --- a/src/EntityDb.Abstractions/Transactions/Builders/ITransactionBuilder.cs +++ /dev/null @@ -1,89 +0,0 @@ -using EntityDb.Abstractions.Leases; -using EntityDb.Abstractions.Tags; -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 transaction step that appends a single command associated 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 . - /// - /// 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 83a9f926..00000000 --- a/src/EntityDb.Abstractions/Transactions/ITransaction.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EntityDb.Abstractions.Transactions.Steps; -using EntityDb.Abstractions.ValueObjects; -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 -{ - /// - /// 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. - /// - /// - /// must be handled in the order they are given. - /// - ImmutableArray Steps { get; } -} 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/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.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/Transactions/Steps/ITransactionStep.cs b/src/EntityDb.Abstractions/Transactions/Steps/ITransactionStep.cs deleted file mode 100644 index e1630845..00000000 --- a/src/EntityDb.Abstractions/Transactions/Steps/ITransactionStep.cs +++ /dev/null @@ -1,24 +0,0 @@ -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; } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/Id.cs b/src/EntityDb.Abstractions/ValueObjects/Id.cs deleted file mode 100644 index 200b26e5..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/Id.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// Represents a unique identifier for an object. -/// -/// 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()); - } - - /// - /// 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(); - } - - /// - /// Implicitly converts an to a pointer. - /// - /// The implicit id argument. - public static implicit operator Pointer(Id id) - { - return id + VersionNumber.MinValue; - } - - /// - /// Combine an Id and a VersionNumber into a Pointer. - /// - /// - /// - /// A pointer for the id and version number. - public static Pointer operator +(Id id, VersionNumber versionNumber) - { - return new Pointer(id, versionNumber); - } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs b/src/EntityDb.Abstractions/ValueObjects/Pointer.cs deleted file mode 100644 index 599cd904..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/Pointer.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace EntityDb.Abstractions.ValueObjects; - -/// -/// Points to an object (e.g., entity, projection) -/// -/// The id of the object. -/// The version number of the object. -public readonly record struct Pointer(Id Id, VersionNumber VersionNumber) -{ - /// - /// Checks if the version number found satisfies the pointer. - /// - /// The actual version number found via queries. - /// true if - public bool IsSatisfiedBy(VersionNumber actualVersionNumber) - { - if (VersionNumber == VersionNumber.MinValue) - { - return actualVersionNumber != VersionNumber.MinValue; - } - - return actualVersionNumber == VersionNumber; - } -} diff --git a/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs b/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs deleted file mode 100644 index 6970371a..00000000 --- a/src/EntityDb.Abstractions/ValueObjects/VersionNumber.cs +++ /dev/null @@ -1,42 +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); - } - - /// - /// 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.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.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/Agents/StandardAgent.cs b/src/EntityDb.Common/Agents/StandardAgent.cs deleted file mode 100644 index 989639aa..00000000 --- a/src/EntityDb.Common/Agents/StandardAgent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Agents; - -internal sealed record StandardAgent(object Signature) : IAgent -{ - public TimeStamp TimeStamp => TimeStamp.UtcNow; -} diff --git a/src/EntityDb.Common/Agents/UnknownAgentSignature.cs b/src/EntityDb.Common/Agents/UnknownAgentSignature.cs deleted file mode 100644 index b10b02a4..00000000 --- a/src/EntityDb.Common/Agents/UnknownAgentSignature.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace EntityDb.Common.Agents; - -/// -/// Represents the signature of an unknown actor. -/// -public record UnknownAgentSignature(Dictionary ApplicationInfo); 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/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/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/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 deleted file mode 100644 index e3448fc9..00000000 --- a/src/EntityDb.Common/Entities/EntityRepository.cs +++ /dev/null @@ -1,104 +0,0 @@ -using EntityDb.Abstractions.Entities; -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 Microsoft.Extensions.DependencyInjection; - -namespace EntityDb.Common.Entities; - -internal class EntityRepository : DisposableResourceBaseClass, IEntityRepository - where TEntity : IEntity -{ - private readonly IEnumerable _transactionSubscribers; - - public EntityRepository - ( - IEnumerable transactionSubscribers, - ITransactionRepository transactionRepository, - ISnapshotRepository? snapshotRepository = null - ) - { - _transactionSubscribers = transactionSubscribers; - TransactionRepository = transactionRepository; - SnapshotRepository = snapshotRepository; - } - - public ITransactionRepository TransactionRepository { 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 commandQuery = new GetEntityCommandsQuery(entityPointer, snapshot.GetVersionNumber()); - - var commands = TransactionRepository.EnumerateCommands(commandQuery, cancellationToken); - - var entity = snapshot; - - await foreach (var command in commands) - { - entity = entity.Reduce(command); - } - - if (!entityPointer.IsSatisfiedBy(entity.GetVersionNumber())) - { - throw new SnapshotPointerDoesNotExistException(); - } - - return entity; - } - - public async Task PutTransaction(ITransaction transaction, CancellationToken cancellationToken = default) - { - var success = await TransactionRepository.PutTransaction(transaction, cancellationToken); - - if (success) - { - Publish(transaction); - } - - return success; - } - - public override async ValueTask DisposeAsync() - { - await TransactionRepository.DisposeAsync(); - - if (SnapshotRepository is not null) - { - await SnapshotRepository.DisposeAsync(); - } - } - - private void Publish(ITransaction transaction) - { - foreach (var transactionSubscriber in _transactionSubscribers) - { - transactionSubscriber.Notify(transaction); - } - } - - public static EntityRepository Create - ( - IServiceProvider serviceProvider, - ITransactionRepository transactionRepository, - ISnapshotRepository? snapshotRepository = null - ) - { - if (snapshotRepository is null) - { - return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionRepository); - } - - return ActivatorUtilities.CreateInstance>(serviceProvider, transactionRepository, - snapshotRepository); - } -} diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs index 0519305c..c7a9b484 100644 --- a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs +++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs @@ -1,44 +1,94 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Entities; -internal class EntityRepositoryFactory : IEntityRepositoryFactory +internal sealed class EntityRepositoryFactory : IEntityRepositoryFactory where TEntity : IEntity { - private readonly IServiceProvider _serviceProvider; - private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly IAgentAccessor _agentAccessor; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + private readonly IStateRepositoryFactory? _stateRepositoryFactory; public EntityRepositoryFactory ( - IServiceProvider serviceProvider, - ITransactionRepositoryFactory transactionRepositoryFactory, - ISnapshotRepositoryFactory? snapshotRepositoryFactory = null + IAgentAccessor agentAccessor, + ISourceRepositoryFactory sourceRepositoryFactory, + IStateRepositoryFactory? stateRepositoryFactory = null ) { - _serviceProvider = serviceProvider; - _transactionRepositoryFactory = transactionRepositoryFactory; - _snapshotRepositoryFactory = snapshotRepositoryFactory; + _agentAccessor = agentAccessor; + _sourceRepositoryFactory = sourceRepositoryFactory; + _stateRepositoryFactory = stateRepositoryFactory; } - public async Task> CreateRepository(string transactionSessionOptionsName, - string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default) + public async Task> CreateSingleForNew + ( + Id entityId, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default + ) + { + var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, + stateSessionOptionsName, cancellationToken); + + multipleEntityRepository.Create(entityId); + + return new SingleEntityRepository(multipleEntityRepository, entityId); + } + + public async Task> CreateSingleForExisting + ( + StatePointer entityPointer, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default + ) + { + var multipleEntityRepository = await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, + stateSessionOptionsName, cancellationToken); + + await multipleEntityRepository.TryLoad(entityPointer, cancellationToken); + + return new SingleEntityRepository(multipleEntityRepository, entityPointer); + } + + public async Task> CreateMultiple + ( + string agentSignatureOptionsName, + string sourceSessionOptionsName, + string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default + ) { - var transactionRepository = - await _transactionRepositoryFactory.CreateRepository(transactionSessionOptionsName, cancellationToken); + var sourceRepository = await _sourceRepositoryFactory + .Create(sourceSessionOptionsName, cancellationToken); - if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) + if (_stateRepositoryFactory is null || stateSessionOptionsName is null) { - return EntityRepository.Create(_serviceProvider, - transactionRepository); + return new MultipleEntityRepository + ( + _agentAccessor, + agentSignatureOptionsName, + sourceRepository + ); } - var snapshotRepository = - await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); + var stateRepository = await _stateRepositoryFactory + .Create(stateSessionOptionsName, cancellationToken); - return EntityRepository.Create(_serviceProvider, - transactionRepository, snapshotRepository); + return new MultipleEntityRepository + ( + _agentAccessor, + agentSignatureOptionsName, + sourceRepository, + stateRepository + ); } } diff --git a/src/EntityDb.Common/Entities/IEntity.cs b/src/EntityDb.Common/Entities/IEntity.cs deleted file mode 100644 index 3c4ceed6..00000000 --- a/src/EntityDb.Common/Entities/IEntity.cs +++ /dev/null @@ -1,17 +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 a new that incorporates the commands. - /// - /// The command - /// A new that incorporates . - TEntity Reduce(object command); -} diff --git a/src/EntityDb.Common/Entities/MultipleEntityRepository.cs b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs new file mode 100644 index 00000000..b00e7f7b --- /dev/null +++ b/src/EntityDb.Common/Entities/MultipleEntityRepository.cs @@ -0,0 +1,162 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Queries.Standard; + +namespace EntityDb.Common.Entities; + +internal sealed class MultipleEntityRepository : DisposableResourceBaseClass, + IMultipleEntityRepository + where TEntity : IEntity +{ + private readonly IAgentAccessor _agentAccessor; + private readonly string _agentSignatureOptionsName; + private readonly Dictionary _knownEntities = new(); + private readonly List _messages = new(); + + public MultipleEntityRepository + ( + IAgentAccessor agentAccessor, + string agentSignatureOptionsName, + ISourceRepository sourceRepository, + IStateRepository? stateRepository = null + ) + { + _agentSignatureOptionsName = agentSignatureOptionsName; + _agentAccessor = agentAccessor; + + SourceRepository = sourceRepository; + StateRepository = stateRepository; + } + + public ISourceRepository SourceRepository { get; } + public IStateRepository? StateRepository { get; } + + public void Create(Id entityId) + { + if (_knownEntities.ContainsKey(entityId)) + { + throw new ExistingEntityException(); + } + + var entity = TEntity.Construct(entityId); + + _knownEntities.Add(entityId, entity); + } + + public async Task TryLoad(StatePointer entityPointer, CancellationToken cancellationToken = default) + { + var entityId = entityPointer.Id; + + if (_knownEntities.TryGetValue(entityId, out var entity)) + { + 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 query = new GetDeltasDataQuery(entityPointer, entity.GetPointer().StateVersion); + + entity = await SourceRepository + .EnumerateDeltas(query, cancellationToken) + .AggregateAsync + ( + entity, + (previousEntity, delta) => previousEntity.Reduce(delta), + cancellationToken + ); + + if (!entityPointer.IsSatisfiedBy(entity.GetPointer())) + { + return false; + } + + _knownEntities.Add(entityId, entity); + + return true; + } + + public TEntity Get(Id entityId) + { + if (!_knownEntities.TryGetValue(entityId, out var entity)) + { + throw new UnknownEntityException(); + } + + return entity; + } + + public void Append(Id entityId, object delta) + { + if (!_knownEntities.TryGetValue(entityId, out var entity)) + { + throw new UnknownEntityException(); + } + + entity = entity.Reduce(delta); + + _messages.Add(Message.NewMessage(entity, entity.GetPointer(), delta)); + + _knownEntities[entityId] = entity; + } + + public async Task Commit(CancellationToken cancellationToken = default) + { + if (_messages.Count == 0) + { + return true; + } + + var agent = await _agentAccessor.GetAgent(_agentSignatureOptionsName, cancellationToken); + + 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 async ValueTask DisposeAsync() + { + await SourceRepository.DisposeAsync(); + + if (StateRepository is not null) + { + await StateRepository.DisposeAsync(); + } + } +} diff --git a/src/EntityDb.Common/Entities/SingleEntityRepository.cs b/src/EntityDb.Common/Entities/SingleEntityRepository.cs new file mode 100644 index 00000000..eb3172dc --- /dev/null +++ b/src/EntityDb.Common/Entities/SingleEntityRepository.cs @@ -0,0 +1,44 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; +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, + StatePointer entityPointer) + { + _multipleEntityRepository = multipleEntityRepository; + + EntityPointer = entityPointer; + } + + public ISourceRepository SourceRepository => _multipleEntityRepository.SourceRepository; + public IStateRepository? StateRepository => _multipleEntityRepository.StateRepository; + public StatePointer EntityPointer { get; } + + public TEntity Get() + { + return _multipleEntityRepository.Get(EntityPointer.Id); + } + + public void Append(object delta) + { + _multipleEntityRepository.Append(EntityPointer.Id, delta); + } + + public Task Commit(CancellationToken cancellationToken = default) + { + return _multipleEntityRepository.Commit(cancellationToken); + } + + public override ValueTask DisposeAsync() + { + return _multipleEntityRepository.DisposeAsync(); + } +} diff --git a/src/EntityDb.Common/EntityDb.Common.csproj b/src/EntityDb.Common/EntityDb.Common.csproj index 1019ed9e..e84fad44 100644 --- a/src/EntityDb.Common/EntityDb.Common.csproj +++ b/src/EntityDb.Common/EntityDb.Common.csproj @@ -1,13 +1,12 @@  - - 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/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/CannotWriteInReadOnlyModeException.cs b/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs deleted file mode 100644 index fddbef98..00000000 --- a/src/EntityDb.Common/Exceptions/CannotWriteInReadOnlyModeException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using EntityDb.Abstractions.Transactions; - -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 -{ -} 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 ce345af7..00000000 --- a/src/EntityDb.Common/Exceptions/DeserializeException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an object envelope cannot be deserialized. Possible objects include: -/// agentSignatures, -/// commands, facts, and leases. -/// -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/ExistingEntityException.cs b/src/EntityDb.Common/Exceptions/ExistingEntityException.cs new file mode 100644 index 00000000..4f5ef5a3 --- /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 519773fb..005048c2 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; @@ -6,6 +6,4 @@ 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/OptimisticConcurrencyException.cs b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs index 3f89bd68..8fe13324 100644 --- a/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs +++ b/src/EntityDb.Common/Exceptions/OptimisticConcurrencyException.cs @@ -1,33 +1,36 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; 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 +/// 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(StateVersion expectedPreviousStateVersion, + StateVersion actualPreviousStateVersion) { - if (expectedPreviousVersionNumber != actualPreviousVersionNumber) + if (expectedPreviousStateVersion != actualPreviousStateVersion) { throw new OptimisticConcurrencyException(); } diff --git a/src/EntityDb.Common/Exceptions/ReadOnlyWriteException.cs b/src/EntityDb.Common/Exceptions/ReadOnlyWriteException.cs new file mode 100644 index 00000000..565293c5 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/ReadOnlyWriteException.cs @@ -0,0 +1,9 @@ +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. +/// +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 5b807cdb..00000000 --- a/src/EntityDb.Common/Exceptions/SerializeException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace EntityDb.Common.Exceptions; - -/// -/// The exception that is thrown when an object envelope cannot be serialized. Possible objects include: -/// agentSignatures, -/// commands, leases, and tags. -/// -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/UnknownEntityException.cs b/src/EntityDb.Common/Exceptions/UnknownEntityException.cs new file mode 100644 index 00000000..9a2dfa2f --- /dev/null +++ b/src/EntityDb.Common/Exceptions/UnknownEntityException.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 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/Exceptions/UnknownStreamKeyException.cs b/src/EntityDb.Common/Exceptions/UnknownStreamKeyException.cs new file mode 100644 index 00000000..117c8df4 --- /dev/null +++ b/src/EntityDb.Common/Exceptions/UnknownStreamKeyException.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 UnknownStreamKeyException : Exception; diff --git a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs b/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs deleted file mode 100644 index fffc3a52..00000000 --- a/src/EntityDb.Common/Exceptions/VersionZeroReservedException.cs +++ /dev/null @@ -1,30 +0,0 @@ -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. -/// -/// -/// Version Zero is reserved for an entity that has not yet been created/persisted. -/// -public 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/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/Extensions/QueryExtensions.cs b/src/EntityDb.Common/Extensions/QueryExtensions.cs deleted file mode 100644 index 4c163ef7..00000000 --- a/src/EntityDb.Common/Extensions/QueryExtensions.cs +++ /dev/null @@ -1,60 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Common.Queries.Modified; - -namespace EntityDb.Common.Extensions; - -/// -/// Extensions for queries. -/// -public static class QueryExtensions -{ - /// - /// Returns a new, modified . The way in which it is modified depends on the - /// parameters of - /// this extension method. - /// - /// The agentSignature query. - /// The options for modifying the query. - /// A new, modified . - public static IAgentSignatureQuery Modify(this IAgentSignatureQuery agentSignatureQuery, - ModifiedQueryOptions modifiedQueryOptions) - { - return new ModifiedAgentSignatureQuery(agentSignatureQuery, modifiedQueryOptions); - } - - /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of - /// this extension method. - /// - /// The command query. - /// The options for modifying the query. - /// A new, modified . - public static ICommandQuery Modify(this ICommandQuery commandQuery, ModifiedQueryOptions modifiedQueryOptions) - { - return new ModifiedCommandQuery(commandQuery, modifiedQueryOptions); - } - - /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of - /// this extension method. - /// - /// The lease query. - /// The options for modifying the query. - /// A new, modified . - public static ILeaseQuery Modify(this ILeaseQuery leaseQuery, ModifiedQueryOptions modifiedQueryOptions) - { - return new ModifiedLeaseQuery(leaseQuery, modifiedQueryOptions); - } - - /// - /// Returns a new, modified . The way in which it is modified depends on the parameters of this - /// extension method. - /// - /// The tag query. - /// The options for modifying the query. - /// A new, modified . - public static ITagQuery Modify(this ITagQuery tagQuery, ModifiedQueryOptions modifiedQueryOptions) - { - return new ModifiedTagQuery(tagQuery, modifiedQueryOptions); - } -} diff --git a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs index 35c3183c..9f1c7438 100644 --- a/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs @@ -1,19 +1,19 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Builders; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Streams; using EntityDb.Common.Entities; using EntityDb.Common.Projections; -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.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; using System.Reflection; -using System.Threading.Tasks.Dataflow; namespace EntityDb.Common.Extensions; @@ -22,24 +22,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 @@ -47,7 +29,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 { @@ -61,32 +44,56 @@ internal static void Add(this IServiceCollection serviceCollection, Se } /// - /// 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 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. + /// Whether or not to run in test mode. [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); + if (testMode) + { + serviceCollection.AddSingleton(); + } + else + { + serviceCollection.AddSingleton(); - serviceCollection.AddSingleton>(); + serviceCollection.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); - serviceCollection.AddSingleton>(); + serviceCollection.AddHostedService(serviceProvider => + serviceProvider.GetRequiredService()); + } + } + /// + /// 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. + /// + /// 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 AddSourceReprocessorQueue(this IServiceCollection serviceCollection, bool testMode = false) + { if (testMode) { - serviceCollection.AddTestModeTransactionProcessorQueue(); + serviceCollection.AddSingleton(); } else { - serviceCollection.AddBufferBlockTransactionProcessorQueue(); + serviceCollection.AddSingleton(); + + serviceCollection.AddHostedService(serviceProvider => + serviceProvider.GetRequiredService()); + + serviceCollection.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); } } @@ -138,33 +145,42 @@ 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) + public static void AddEntityRepository(this IServiceCollection serviceCollection) where TEntity : IEntity { - serviceCollection.AddTransient, TransactionBuilderFactory>(); - serviceCollection.AddTransient, EntityRepositoryFactory>(); } /// - /// Adds a transaction subscriber that records snapshots of entities. + /// Adds a source subscriber that records states 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 agent's intent for the source repository. + /// The agent's intent for the state repository. /// The type of the entity. - public static void AddEntitySnapshotTransactionSubscriber(this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false) + public static void AddEntityStateSourceSubscriber(this IServiceCollection serviceCollection, + string sourceSessionOptionsName, string stateSessionOptionsName) where TEntity : IEntity { - serviceCollection.AddTransactionProcessorSubscriber(testMode, serviceProvider => EntitySnapshotTransactionProcessor.Create( - serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); + serviceCollection.AddSingleton>(); + serviceCollection.AddScoped(serviceProvider => + EntityStateSourceProcessor.Create(serviceProvider, sourceSessionOptionsName, + stateSessionOptionsName)); + } + + /// + /// Adds a transient implementation of + /// to a service collection. + /// + /// The service collection. + public static void AddStreamRepository(this IServiceCollection serviceCollection) + { + serviceCollection.AddTransient(); } /// @@ -172,7 +188,7 @@ public static void AddEntitySnapshotTransactionSubscriber(this IService /// /// The service collection. /// The type of the projection. - public static void AddProjection( + public static void AddProjectionRepository( this IServiceCollection serviceCollection) where TProjection : IProjection { @@ -181,19 +197,18 @@ public static void AddProjection( } /// - /// Adds a transaction subscriber that records snapshots of projections. + /// Adds a source subscriber that records states 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 agent's intent for the state repository. /// The type of the projection. - public static void AddProjectionSnapshotTransactionSubscriber( + public static void AddProjectionStateSourceSubscriber( this IServiceCollection serviceCollection, - string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false) + string stateSessionOptionsName) where TProjection : IProjection { - serviceCollection.AddTransactionProcessorSubscriber(testMode, serviceProvider => ProjectionSnapshotTransactionProcessor.Create( - serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName)); + serviceCollection.AddSingleton>(); + serviceCollection.AddScoped(serviceProvider => + 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/Extensions/SortBuilderExtensions.cs b/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs deleted file mode 100644 index 7bb1222a..00000000 --- a/src/EntityDb.Common/Extensions/SortBuilderExtensions.cs +++ /dev/null @@ -1,72 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Common.Queries.SortBuilders; - -namespace EntityDb.Common.Extensions; - -/// -/// Extensions for sort builders. -/// -public static class SortBuilderExtensions -{ - /// - /// Returns a that orders agentSignatures in the reverse order of - /// another - /// . - /// - /// The type of sort used by the repository. - /// The agentSignature sort builder. - /// - /// A that orders agentSignatures in the reverse order of - /// . - /// - public static IAgentSignatureSortBuilder Reverse( - this IAgentSignatureSortBuilder agentSignatureSortBuilder) - { - return new AgentSignatureReverseSortBuilder(agentSignatureSortBuilder); - } - - /// - /// Returns a that orders commands in the reverse order of another - /// . - /// - /// The type of sort used by the repository. - /// The command sort builder. - /// - /// A that orders commands in the reverse order of - /// . - /// - public static ICommandSortBuilder Reverse(this ICommandSortBuilder commandSortBuilder) - { - return new CommandReverseSortBuilder(commandSortBuilder); - } - - /// - /// 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 - /// . - /// - public static ILeaseSortBuilder Reverse(this ILeaseSortBuilder leaseSortBuilder) - { - return new LeaseReverseSortBuilder(leaseSortBuilder); - } - - /// - /// 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 - /// . - /// - public static ITagSortBuilder Reverse(this ITagSortBuilder tagSortBuilder) - { - return new TagReverseSortBuilder(tagSortBuilder); - } -} diff --git a/src/EntityDb.Common/Leases/Lease.cs b/src/EntityDb.Common/Leases/Lease.cs deleted file mode 100644 index b0544e98..00000000 --- a/src/EntityDb.Common/Leases/Lease.cs +++ /dev/null @@ -1,6 +0,0 @@ -using EntityDb.Abstractions.Leases; - -namespace EntityDb.Common.Leases; - -/// -public sealed record Lease(string Scope, string Label, string Value) : ILease; diff --git a/src/EntityDb.Common/Projections/IProjection.cs b/src/EntityDb.Common/Projections/IProjection.cs deleted file mode 100644 index 3706a13d..00000000 --- a/src/EntityDb.Common/Projections/IProjection.cs +++ /dev/null @@ -1,34 +0,0 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Queries; -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 -{ - /// - /// Returns a new that incorporates the command for a particular entity id. - /// - /// The annotated command. - /// A new that incorporates . - TProjection Reduce(IEntityAnnotation annotatedCommand); - - /// - /// Returns a that is used to load the rest of the state for the given projection pointer. - /// - /// 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); - - /// - /// Maps an entity to a projection id, or default if the entity does not map to this projection. - /// - /// The entity object. - /// The projection id for the entity, or default if none. - static abstract Id? GetProjectionIdOrDefault(object entity); -} diff --git a/src/EntityDb.Common/Projections/ProjectionRepository.cs b/src/EntityDb.Common/Projections/ProjectionRepository.cs index a36af76d..8156ff63 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepository.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepository.cs @@ -1,10 +1,10 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; namespace EntityDb.Common.Projections; @@ -12,60 +12,85 @@ internal sealed class ProjectionRepository : DisposableResourceBase IProjectionRepository where TProjection : IProjection { + private readonly IServiceProvider _serviceProvider; + private readonly Dictionary _knownProjections = new(); + public ProjectionRepository ( - ITransactionRepository transactionRepository, - ISnapshotRepository? snapshotRepository = null + IServiceProvider serviceProvider, + IStateRepository? stateRepository = null ) { - TransactionRepository = transactionRepository; - SnapshotRepository = snapshotRepository; + _serviceProvider = serviceProvider; + StateRepository = stateRepository; } - public ITransactionRepository TransactionRepository { get; } - - public ISnapshotRepository? SnapshotRepository { get; } + public IStateRepository? StateRepository { get; } - public Id? GetProjectionIdOrDefault(object entity) + public async Task TryLoad(StatePointer projectionPointer, CancellationToken cancellationToken = default) { - return TProjection.GetProjectionIdOrDefault(entity); - } + var projectionId = projectionPointer.Id; + + if (_knownProjections.TryGetValue(projectionId, out var projection)) + { + var knownProjectionPointer = projection.GetPointer(); - public async Task GetSnapshot(Pointer projectionPointer, CancellationToken cancellationToken = default) - { - var projection = SnapshotRepository is not null - ? await SnapshotRepository.GetSnapshotOrDefault(projectionPointer, cancellationToken) ?? - TProjection.Construct(projectionPointer.Id) - : TProjection.Construct(projectionPointer.Id); + 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 commandQuery = projection.GetCommandQuery(projectionPointer); + var sources = projection.EnumerateSources(_serviceProvider, projectionPointer, cancellationToken); - var annotatedCommands = TransactionRepository.EnumerateAnnotatedCommands(commandQuery, cancellationToken); + await foreach (var source in sources) + { + projection.Mutate(source); + } - await foreach (var annotatedCommand in annotatedCommands) + if (!projectionPointer.IsSatisfiedBy(projection.GetPointer())) { - projection = projection.Reduce(annotatedCommand); + return false; } - if (!projectionPointer.IsSatisfiedBy(projection.GetVersionNumber())) + _knownProjections.Add(projectionId, projection); + + return true; + } + + public TProjection Get(Id projectionId) + { + if (!_knownProjections.TryGetValue(projectionId, out var projection)) { - throw new SnapshotPointerDoesNotExistException(); + throw new UnknownProjectionException(); } return projection; } public static IProjectionRepository Create(IServiceProvider serviceProvider, - ITransactionRepository transactionRepository, - ISnapshotRepository? snapshotRepository = null) + IStateRepository? stateRepository = null) { - if (snapshotRepository is null) + if (stateRepository is null) { - return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionRepository); + return ActivatorUtilities.CreateInstance>(serviceProvider); } return ActivatorUtilities.CreateInstance>(serviceProvider, - transactionRepository, snapshotRepository); + stateRepository); } } diff --git a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs index 2db48c92..e68dbede 100644 --- a/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs +++ b/src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs @@ -1,44 +1,39 @@ using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.States; namespace EntityDb.Common.Projections; -internal class ProjectionRepositoryFactory : IProjectionRepositoryFactory +internal sealed class ProjectionRepositoryFactory : IProjectionRepositoryFactory where TProjection : IProjection { private readonly IServiceProvider _serviceProvider; - private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory; - private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; + private readonly IStateRepositoryFactory? _stateRepositoryFactory; public ProjectionRepositoryFactory ( IServiceProvider serviceProvider, - ITransactionRepositoryFactory transactionRepositoryFactory, - ISnapshotRepositoryFactory? snapshotRepositoryFactory = null + IStateRepositoryFactory? stateRepositoryFactory = null ) { _serviceProvider = serviceProvider; - _transactionRepositoryFactory = transactionRepositoryFactory; - _snapshotRepositoryFactory = snapshotRepositoryFactory; + _stateRepositoryFactory = stateRepositoryFactory; } - public async Task> CreateRepository(string transactionSessionOptionsName, - string? snapshotSessionOptionsName = null, CancellationToken cancellationToken = default) + public async Task> CreateRepository + ( + string? stateSessionOptionsName = null, + CancellationToken cancellationToken = default + ) { - var transactionRepository = - await _transactionRepositoryFactory.CreateRepository(transactionSessionOptionsName, cancellationToken); - - if (_snapshotRepositoryFactory is null || snapshotSessionOptionsName is null) + if (_stateRepositoryFactory is null || stateSessionOptionsName is null) { - return ProjectionRepository.Create(_serviceProvider, - transactionRepository); + return ProjectionRepository.Create(_serviceProvider); } - var snapshotRepository = - await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName, cancellationToken); + var stateRepository = + await _stateRepositoryFactory.Create(stateSessionOptionsName, cancellationToken); return ProjectionRepository.Create(_serviceProvider, - transactionRepository, snapshotRepository); + stateRepository); } } diff --git a/src/EntityDb.Common/Properties/AssemblyInfo.cs b/src/EntityDb.Common/Properties/AssemblyInfo.cs index 912c98c8..60dcbeb4 100644 --- a/src/EntityDb.Common/Properties/AssemblyInfo.cs +++ b/src/EntityDb.Common/Properties/AssemblyInfo.cs @@ -1,15 +1,11 @@ using System.Runtime.CompilerServices; // src -[assembly: InternalsVisibleTo("EntityDb.SqlDb")] -[assembly: InternalsVisibleTo("EntityDb.Npgsql")] -[assembly: InternalsVisibleTo("EntityDb.EntityFramework")] -[assembly: InternalsVisibleTo("EntityDb.InMemory")] +[assembly: InternalsVisibleTo("EntityDb.Aws")] [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/DeleteTagsQuery.cs b/src/EntityDb.Common/Queries/DeleteTagsQuery.cs deleted file mode 100644 index 36d80348..00000000 --- a/src/EntityDb.Common/Queries/DeleteTagsQuery.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Abstractions.Tags; -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Queries; - -internal sealed record DeleteTagsQuery(Id EntityId, IReadOnlyCollection Tags, object? Options = null) : ITagQuery -{ - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.And - ( - builder.EntityIdIn(EntityId), - builder.Or - ( - Tags - .Select(deleteTag => builder.And( - builder.TagLabelEq(deleteTag.Label), - builder.TagValueEq(deleteTag.Value) - )) - .ToArray() - ) - ); - } - - public TSort? GetSort(ITagSortBuilder builder) - { - return default; - } - - public int? Skip => null; - - public int? Take => 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/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/Modified/ModifiedLeaseQuery.cs b/src/EntityDb.Common/Queries/Modified/ModifiedLeaseQuery.cs deleted file mode 100644 index 45f76c10..00000000 --- a/src/EntityDb.Common/Queries/Modified/ModifiedLeaseQuery.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 ModifiedLeaseQuery - (ILeaseQuery LeaseQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(LeaseQuery, - ModifiedQueryOptions), ILeaseQuery -{ - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - if (ModifiedQueryOptions.InvertFilter) - { - return builder.Not - ( - LeaseQuery.GetFilter(builder) - ); - } - - return LeaseQuery.GetFilter(builder); - } - - public TSort? GetSort(ILeaseSortBuilder builder) - { - return LeaseQuery.GetSort(ModifiedQueryOptions.ReverseSort - ? builder.Reverse() - : builder); - } -} diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedQueryBase.cs b/src/EntityDb.Common/Queries/Modified/ModifiedQueryBase.cs deleted file mode 100644 index 21e7c06f..00000000 --- a/src/EntityDb.Common/Queries/Modified/ModifiedQueryBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EntityDb.Abstractions.Queries; - -namespace EntityDb.Common.Queries.Modified; - -internal abstract record ModifiedQueryBase(IQuery Query, ModifiedQueryOptions ModifiedQueryOptions) -{ - public int? Skip => ModifiedQueryOptions.ReplaceSkip ?? Query.Skip; - - public int? Take => ModifiedQueryOptions.ReplaceTake ?? Query.Take; - - public object? Options => Query.Options; -} diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedTagQuery.cs b/src/EntityDb.Common/Queries/Modified/ModifiedTagQuery.cs deleted file mode 100644 index 525eed2f..00000000 --- a/src/EntityDb.Common/Queries/Modified/ModifiedTagQuery.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 ModifiedTagQuery - (ITagQuery TagQuery, ModifiedQueryOptions ModifiedQueryOptions) : ModifiedQueryBase(TagQuery, - ModifiedQueryOptions), ITagQuery -{ - public TFilter GetFilter(ITagFilterBuilder builder) - { - if (ModifiedQueryOptions.InvertFilter) - { - return builder.Not - ( - TagQuery.GetFilter(builder) - ); - } - - return TagQuery.GetFilter(builder); - } - - public TSort? GetSort(ITagSortBuilder builder) - { - return TagQuery.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/Queries/SortBuilders/LeaseReverseSortBuilder.cs b/src/EntityDb.Common/Queries/SortBuilders/LeaseReverseSortBuilder.cs deleted file mode 100644 index 51cfc169..00000000 --- a/src/EntityDb.Common/Queries/SortBuilders/LeaseReverseSortBuilder.cs +++ /dev/null @@ -1,38 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; - -namespace EntityDb.Common.Queries.SortBuilders; - -internal sealed record LeaseReverseSortBuilder - (ILeaseSortBuilder LeaseSortBuilder) : ReverseSortBuilderBase(LeaseSortBuilder), - ILeaseSortBuilder -{ - public TSort EntityId(bool ascending) - { - return LeaseSortBuilder.EntityId(!ascending); - } - - public TSort EntityVersionNumber(bool ascending) - { - return LeaseSortBuilder.EntityVersionNumber(!ascending); - } - - public TSort LeaseType(bool ascending) - { - return LeaseSortBuilder.LeaseType(!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/Queries/SortBuilders/ReverseSortBuilderBase.cs b/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs deleted file mode 100644 index a5429608..00000000 --- a/src/EntityDb.Common/Queries/SortBuilders/ReverseSortBuilderBase.cs +++ /dev/null @@ -1,21 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; - -namespace EntityDb.Common.Queries.SortBuilders; - -internal abstract record ReverseSortBuilderBase(ISortBuilder SortBuilder) -{ - public TSort TransactionTimeStamp(bool ascending) - { - return SortBuilder.TransactionTimeStamp(!ascending); - } - - public TSort TransactionId(bool ascending) - { - return SortBuilder.TransactionId(!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/Queries/SortBuilders/TagReverseSortBuilder.cs deleted file mode 100644 index 68243616..00000000 --- a/src/EntityDb.Common/Queries/SortBuilders/TagReverseSortBuilder.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; - -namespace EntityDb.Common.Queries.SortBuilders; - -internal sealed record TagReverseSortBuilder - (ITagSortBuilder TagSortBuilder) : ReverseSortBuilderBase(TagSortBuilder), ITagSortBuilder -{ - public TSort EntityId(bool ascending) - { - return TagSortBuilder.EntityId(!ascending); - } - - public TSort EntityVersionNumber(bool ascending) - { - return TagSortBuilder.EntityVersionNumber(!ascending); - } - - public TSort TagType(bool ascending) - { - return TagSortBuilder.TagType(!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/Snapshots/ISnapshot.cs b/src/EntityDb.Common/Snapshots/ISnapshot.cs deleted file mode 100644 index 11fd26a3..00000000 --- a/src/EntityDb.Common/Snapshots/ISnapshot.cs +++ /dev/null @@ -1,46 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.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 id of the snapshot. - /// A new instance of . - static abstract TSnapshot Construct(Id snapshotId); - - /// - /// Returns the id of this snapshot. - /// - /// The id of this snapshot. - Id GetId(); - - /// - /// Returns the version number of this snapshot. - /// - /// The version number of this snapshot. - VersionNumber GetVersionNumber(); - - /// - /// 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. - /// - 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.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/TestModeRedisSnapshotRepository.cs b/src/EntityDb.Common/Snapshots/TestModeRedisSnapshotRepository.cs deleted file mode 100644 index 809b5e95..00000000 --- a/src/EntityDb.Common/Snapshots/TestModeRedisSnapshotRepository.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/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/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/Agents/AgentAccessorChain.cs b/src/EntityDb.Common/Sources/Agents/AgentAccessorChain.cs similarity index 85% rename from src/EntityDb.Common/Agents/AgentAccessorChain.cs rename to src/EntityDb.Common/Sources/Agents/AgentAccessorChain.cs index a6d9f5d2..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 @@ -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; @@ -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/Sources/Agents/StandardAgent.cs b/src/EntityDb.Common/Sources/Agents/StandardAgent.cs new file mode 100644 index 00000000..9f5c6fa0 --- /dev/null +++ b/src/EntityDb.Common/Sources/Agents/StandardAgent.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; + +namespace EntityDb.Common.Sources.Agents; + +internal sealed record StandardAgent(object Signature) : IAgent +{ + public TimeStamp TimeStamp => TimeStamp.UtcNow; +} diff --git a/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs b/src/EntityDb.Common/Sources/Agents/UnknownAgentAccessor.cs similarity index 69% rename from src/EntityDb.Common/Agents/UnknownAgentAccessor.cs rename to src/EntityDb.Common/Sources/Agents/UnknownAgentAccessor.cs index c06be995..d492746f 100644 --- a/src/EntityDb.Common/Agents/UnknownAgentAccessor.cs +++ b/src/EntityDb.Common/Sources/Agents/UnknownAgentAccessor.cs @@ -1,11 +1,11 @@ -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. /// -public class UnknownAgentAccessor : IAgentAccessor +public sealed class UnknownAgentAccessor : IAgentAccessor { private static readonly Dictionary DefaultApplicationInfo = new(); @@ -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/Sources/Agents/UnknownAgentSignature.cs b/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs new file mode 100644 index 00000000..6407e1f7 --- /dev/null +++ b/src/EntityDb.Common/Sources/Agents/UnknownAgentSignature.cs @@ -0,0 +1,6 @@ +namespace EntityDb.Common.Sources.Agents; + +/// +/// Represents the signature of an unknown actor. +/// +public sealed record UnknownAgentSignature(Dictionary ApplicationInfo); diff --git a/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs new file mode 100644 index 00000000..344b96fc --- /dev/null +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedMessageData.cs @@ -0,0 +1,38 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Sources.Annotations; + +internal sealed record AnnotatedMessageData +( + Id SourceId, + TimeStamp SourceTimeStamp, + Id MessageId, + TData Data, + StatePointer StatePointer +) : IAnnotatedMessageData +{ + public static IAnnotatedMessageData CreateFromBoxedData + ( + Id sourceId, + TimeStamp sourceTimeStamp, + Id messageId, + object boxedData, + StatePointer 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 new file mode 100644 index 00000000..b0d334dd --- /dev/null +++ b/src/EntityDb.Common/Sources/Annotations/AnnotatedSourceData.cs @@ -0,0 +1,38 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Annotations; +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Sources.Annotations; + +internal sealed record AnnotatedSourceData +( + Id SourceId, + TimeStamp SourceTimeStamp, + Id[] MessageIds, + TData Data, + StatePointer[] StatePointers +) : IAnnotatedSourceData +{ + public static IAnnotatedSourceData CreateFromBoxedData + ( + Id sourceId, + TimeStamp sourceTimeStamp, + Id[] messageIds, + object boxedData, + StatePointer[] statePointers + ) + { + var dataAnnotationType = typeof(AnnotatedSourceData<>).MakeGenericType(boxedData.GetType()); + + return (IAnnotatedSourceData)Activator.CreateInstance + ( + dataAnnotationType, + sourceId, + sourceTimeStamp, + messageIds, + boxedData, + statePointers + )!; + } +} 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/Extensions/DocumentsExtensions.cs b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs similarity index 50% rename from src/EntityDb.Common/Extensions/DocumentsExtensions.cs rename to src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs index 9e71994e..0db77e5b 100644 --- a/src/EntityDb.Common/Extensions/DocumentsExtensions.cs +++ b/src/EntityDb.Common/Sources/Documents/DocumentsExtensions.cs @@ -1,11 +1,11 @@ -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Annotations; -using EntityDb.Common.Documents; +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; -namespace EntityDb.Common.Extensions; +namespace EntityDb.Common.Sources.Documents; internal static class DocumentsExtensions { @@ -34,13 +34,39 @@ Func, IAsyncEnumerable> mapToIds return ids; } - public static async IAsyncEnumerable> EnumerateEntityAnnotation + public static IAsyncEnumerable EnumeratePointers + ( + this IAsyncEnumerable documents, + int? skip, + int? limit, + Func, IAsyncEnumerable> mapToStatePointers + ) + { + var statePointers = mapToStatePointers + .Invoke(documents) + .Distinct(); + + if (skip.HasValue) + { + statePointers = statePointers.Skip(skip.Value); + } + + if (limit.HasValue) + { + statePointers = statePointers.Take(limit.Value); + } + + return statePointers; + } + + public static async IAsyncEnumerable> EnumerateAnnotatedSourceData ( this IAsyncEnumerable documents, IEnvelopeService envelopeService, [EnumeratorCancellation] CancellationToken cancellationToken ) - where TDocument : IEntityDocument + where TDocument : IMessageDataDocument where TData : notnull { await using var enumerator = documents.GetAsyncEnumerator(cancellationToken); @@ -49,24 +75,25 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var document = enumerator.Current; - yield return EntityAnnotation.CreateFromBoxedData + yield return AnnotatedMessageData.CreateFromBoxedData ( - document.TransactionId, - document.TransactionTimeStamp, - document.EntityId, - document.EntityVersionNumber, - envelopeService.Deserialize(document.Data) + document.SourceId, + document.SourceTimeStamp, + document.MessageId, + envelopeService.Deserialize(document.Data), + document.StatePointer ); } } - public static async IAsyncEnumerable> EnumerateEntitiesAnnotation + public static async IAsyncEnumerable> EnumerateEntitiesAnnotation ( this IAsyncEnumerable documents, IEnvelopeService envelopeService, [EnumeratorCancellation] CancellationToken cancellationToken ) - where TDocument : IEntitiesDocument + where TDocument : ISourceDataDocument where TData : notnull { await using var enumerator = documents.GetAsyncEnumerator(cancellationToken); @@ -75,12 +102,13 @@ [EnumeratorCancellation] CancellationToken cancellationToken { var document = enumerator.Current; - yield return EntitiesAnnotation.CreateFromBoxedData + yield return AnnotatedSourceData.CreateFromBoxedData ( - document.TransactionId, - document.TransactionTimeStamp, - document.EntityIds, - envelopeService.Deserialize(document.Data) + document.SourceId, + document.SourceTimeStamp, + document.MessageIds, + envelopeService.Deserialize(document.Data), + document.StatePointers ); } } diff --git a/src/EntityDb.Common/Sources/Documents/IDocument.cs b/src/EntityDb.Common/Sources/Documents/IDocument.cs new file mode 100644 index 00000000..53167b53 --- /dev/null +++ b/src/EntityDb.Common/Sources/Documents/IDocument.cs @@ -0,0 +1,11 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; + +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/IMessageDataDocument.cs b/src/EntityDb.Common/Sources/Documents/IMessageDataDocument.cs new file mode 100644 index 00000000..936c384a --- /dev/null +++ b/src/EntityDb.Common/Sources/Documents/IMessageDataDocument.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Sources.Documents; + +internal interface IMessageDataDocument : IDocument +{ + Id MessageId { get; } + StatePointer StatePointer { get; } +} diff --git a/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs b/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs new file mode 100644 index 00000000..0b52cc45 --- /dev/null +++ b/src/EntityDb.Common/Sources/Documents/ISourceDataDocument.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Sources.Documents; + +internal interface ISourceDataDocument : IDocument +{ + Id[] MessageIds { get; } + StatePointer[] StatePointers { get; } +} diff --git a/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs new file mode 100644 index 00000000..ab6e2b52 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/EntityStateSourceProcessor.cs @@ -0,0 +1,112 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.Processors; + +/// +/// A source processor that persists entity states +/// +/// The type of the entity +public sealed class EntityStateSourceProcessor : ISourceProcessor + where TEntity : IEntity +{ + private readonly IEntityRepositoryFactory _entityRepositoryFactory; + private readonly ILogger> _logger; + private readonly string _sourceSessionOptionsName; + private readonly string _stateSessionOptionsName; + + /// + public EntityStateSourceProcessor + ( + ILogger> logger, + IEntityRepositoryFactory entityRepositoryFactory, + string sourceSessionOptionsName, + string stateSessionOptionsName + ) + { + _logger = logger; + _entityRepositoryFactory = entityRepositoryFactory; + _sourceSessionOptionsName = sourceSessionOptionsName; + _stateSessionOptionsName = stateSessionOptionsName; + } + + /// + public async Task Process(Source source, CancellationToken cancellationToken) + { + if (source.Messages.Any(message => !TEntity.CanReduce(message.Delta))) + { + throw new NotSupportedException(); + } + + await using var entityRepository = await _entityRepositoryFactory + .CreateMultiple(default!, _sourceSessionOptionsName, _stateSessionOptionsName, cancellationToken); + + if (entityRepository.StateRepository is null) + { + _logger.LogWarning("State repository not available, skipping source processing."); + + return; + } + + var persistEntities = new Dictionary(); + + var entityMessageGroups = source.Messages + .GroupBy(message => message.StatePointer.Id); + + 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 = TEntity.Construct(entityId); + } + else if (await entityRepository.TryLoad(previousEntityPointer, cancellationToken)) + { + previousEntity = entityRepository.Get(entityId); + } + else + { + 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; + } + } + + foreach (var (entityPointer, entity) in persistEntities) + { + await entityRepository.StateRepository.Put(entityPointer, entity, cancellationToken); + } + } + + internal static EntityStateSourceProcessor Create(IServiceProvider serviceProvider, + string sourceSessionOptionsName, string stateSessionOptionsName) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, + sourceSessionOptionsName, stateSessionOptionsName); + } +} diff --git a/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ISourceProcessor.cs new file mode 100644 index 00000000..7ed1cb76 --- /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(Source source, CancellationToken cancellationToken); +} diff --git a/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs b/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs new file mode 100644 index 00000000..ca69fdea --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/ProjectionStateSourceProcessor.cs @@ -0,0 +1,73 @@ +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 projectionId in TProjection.EnumerateProjectionIds(source).Distinct()) + { + var projection = await projectionRepository.TryLoad(projectionId, cancellationToken) + ? projectionRepository.Get(projectionId) + : TProjection.Construct(projectionId); + + projection.Mutate(source); + + if (projection.ShouldPersist()) + { + await projectionRepository.StateRepository.Put(projection.GetPointer(), projection, cancellationToken); + } + + if (projection.ShouldPersistAsLatest()) + { + await projectionRepository.StateRepository.Put(projectionId, projection, cancellationToken); + } + + cancellationToken.ThrowIfCancellationRequested(); + } + } + + internal static ProjectionStateSourceProcessor Create(IServiceProvider serviceProvider, + string stateSessionOptionsName) + { + return ActivatorUtilities.CreateInstance>(serviceProvider, + stateSessionOptionsName); + } +} 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..05585d76 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/BufferBlockSourceProcessorQueue.cs @@ -0,0 +1,58 @@ +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 sealed 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..b1bde5f9 --- /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. + /// + Source Source { get; } +} diff --git a/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueExtensions.cs b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueExtensions.cs new file mode 100644 index 00000000..16daeee0 --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/SourceProcessorQueueExtensions.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Sources; + +namespace EntityDb.Common.Sources.Processors.Queues; + +/// +/// Extensions for . +/// +public static class SourceProcessorQueueExtensions +{ + /// + /// Adds a source and the corresponding processor to the queue. + /// + /// 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, + }); + } +} 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..20c77377 --- /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 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 new file mode 100644 index 00000000..22504c9e --- /dev/null +++ b/src/EntityDb.Common/Sources/Processors/Queues/TestModeSourceProcessorQueue.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.Processors.Queues; + +internal sealed class TestModeSourceProcessorQueue : ISourceProcessorQueue +{ + private readonly ILogger _logger; + private readonly 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), + }); + + 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"); + } + } +} diff --git a/src/EntityDb.Common/Sources/PublishSourceRepository.cs b/src/EntityDb.Common/Sources/PublishSourceRepository.cs new file mode 100644 index 00000000..a81c6c89 --- /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/Extensions/FilterBuilderExtensions.cs b/src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs similarity index 63% rename from src/EntityDb.Common/Extensions/FilterBuilderExtensions.cs rename to src/EntityDb.Common/Sources/Queries/FilterBuilders/FilterBuilderExtensions.cs index 05d9c287..c32167f9 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. @@ -11,17 +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, params TFilter[] filters) + public static TFilter Nand(this IDataFilterBuilder dataFilterBuilder, + params TFilter[] filters) { - return filterBuilder.Not + return dataFilterBuilder.Not ( - filterBuilder.And(filters) + dataFilterBuilder.And(filters) ); } @@ -29,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) ); } @@ -47,25 +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, TFilter filterB) + 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 ) ); @@ -75,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/ModifiedLeaseDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataQuery.cs new file mode 100644 index 00000000..8be2b6a9 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedLeaseDataQuery.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 ModifiedLeaseDataQuery : ModifiedQueryBase, ILeaseDataQuery +{ + public required ILeaseDataQuery LeaseDataQuery { get; init; } + protected override IDataQuery DataQuery => LeaseDataQuery; + + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + if (ModifiedQueryOptions.InvertFilter) + { + return builder.Not + ( + LeaseDataQuery.GetFilter(builder) + ); + } + + return LeaseDataQuery.GetFilter(builder); + } + + public TSort? GetSort(ILeaseDataSortBuilder builder) + { + return LeaseDataQuery.GetSort(ModifiedQueryOptions.ReverseSort + ? builder.Reverse() + : builder); + } +} 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..6e58577b --- /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 IDataQuery DataQuery => 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/ModifiedQueryBase.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs new file mode 100644 index 00000000..3df744d2 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryBase.cs @@ -0,0 +1,15 @@ +using EntityDb.Abstractions.Sources.Queries; + +namespace EntityDb.Common.Sources.Queries.Modified; + +internal abstract record ModifiedQueryBase +{ + protected abstract IDataQuery DataQuery { get; } + public required ModifiedQueryOptions ModifiedQueryOptions { get; init; } + + public int? Skip => ModifiedQueryOptions.ReplaceSkip ?? DataQuery.Skip; + + public int? Take => ModifiedQueryOptions.ReplaceTake ?? DataQuery.Take; + + public object? Options => DataQuery.Options; +} diff --git a/src/EntityDb.Common/Queries/Modified/ModifiedQueryOptions.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs similarity index 60% rename from src/EntityDb.Common/Queries/Modified/ModifiedQueryOptions.cs rename to src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs index 0062d92d..76adb79d 100644 --- a/src/EntityDb.Common/Queries/Modified/ModifiedQueryOptions.cs +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedQueryOptions.cs @@ -1,16 +1,16 @@ -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 . /// -public record ModifiedQueryOptions +public sealed 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. /// @@ -23,14 +23,16 @@ 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/ModifiedSourceDataQuery.cs new file mode 100644 index 00000000..7d2e239f --- /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 IDataQuery DataQuery => 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/ModifiedTagDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataQuery.cs new file mode 100644 index 00000000..e6df3874 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Modified/ModifiedTagDataQuery.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 ModifiedTagDataQuery : ModifiedQueryBase, ITagDataQuery +{ + public required ITagDataQuery TagDataQuery { get; init; } + protected override IDataQuery DataQuery => TagDataQuery; + + public TFilter GetFilter(ITagDataFilterBuilder builder) + { + if (ModifiedQueryOptions.InvertFilter) + { + return builder.Not + ( + TagDataQuery.GetFilter(builder) + ); + } + + return TagDataQuery.GetFilter(builder); + } + + public TSort? GetSort(ITagDataSortBuilder builder) + { + 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 new file mode 100644 index 00000000..c2a22081 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/QueryExtensions.cs @@ -0,0 +1,72 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Common.Sources.Queries.Modified; + +namespace EntityDb.Common.Sources.Queries; + +/// +/// Extensions for queries. +/// +public static class QueryExtensions +{ + /// + /// Returns a new, modified . The way in which + /// it is modified depends on the parameters of this extension method. + /// + /// The source data query. + /// The options for modifying the query. + /// A new, modified . + public static ISourceDataQuery Modify(this ISourceDataQuery sourceDataQuery, + ModifiedQueryOptions modifiedQueryOptions) + { + return new ModifiedSourceDataQuery + { + ModifiedQueryOptions = modifiedQueryOptions, SourceDataQuery = sourceDataQuery, + }; + } + + /// + /// Returns a new, modified . The way in which it is modified depends on the parameters + /// of + /// this extension method. + /// + /// The message data query. + /// The options for modifying the query. + /// A new, modified . + public static IMessageDataQuery Modify(this IMessageDataQuery messageDataQuery, + ModifiedQueryOptions modifiedQueryOptions) + { + return new ModifiedMessageDataQuery + { + ModifiedQueryOptions = modifiedQueryOptions, MessageDataQuery = messageDataQuery, + }; + } + + /// + /// Returns a new, modified . The way in which it is modified depends on the parameters + /// of + /// this extension method. + /// + /// The lease query. + /// The options for modifying the query. + /// A new, modified . + public static ILeaseDataQuery Modify(this ILeaseDataQuery leaseDataQuery, ModifiedQueryOptions modifiedQueryOptions) + { + return new ModifiedLeaseDataQuery + { + ModifiedQueryOptions = modifiedQueryOptions, LeaseDataQuery = leaseDataQuery, + }; + } + + /// + /// Returns a new, modified . The way in which it is modified depends on the parameters of + /// this + /// extension method. + /// + /// The tag query. + /// The options for modifying the query. + /// A new, modified . + public static ITagDataQuery Modify(this ITagDataQuery tagDataQuery, ModifiedQueryOptions modifiedQueryOptions) + { + 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 new file mode 100644 index 00000000..f42d7c7a --- /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 IDataSortBuilder DataSortBuilder => 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/ReverseMessageDataSortBuilder.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseMessageDataSortBuilder.cs new file mode 100644 index 00000000..e6357ea4 --- /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 IDataSortBuilder DataSortBuilder => 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/ReverseSortBuilderBase.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs new file mode 100644 index 00000000..86e8e172 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/ReverseSortBuilderBase.cs @@ -0,0 +1,28 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +internal abstract record ReverseSortBuilderBase +{ + protected abstract IDataSortBuilder DataSortBuilder { get; } + + public TSort SourceTimeStamp(bool ascending) + { + return DataSortBuilder.SourceTimeStamp(!ascending); + } + + public TSort SourceId(bool ascending) + { + return DataSortBuilder.SourceId(!ascending); + } + + public TSort DataType(bool ascending) + { + return DataSortBuilder.DataType(!ascending); + } + + public TSort Combine(params TSort[] 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 new file mode 100644 index 00000000..432be1d6 --- /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 IDataSortBuilder DataSortBuilder => SourceDataSortBuilder; + + public TSort StateIds(bool ascending) + { + return SourceDataSortBuilder.StateIds(!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..e2d98321 --- /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 IDataSortBuilder DataSortBuilder => 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/SortBuilderExtensions.cs b/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs new file mode 100644 index 00000000..c80ba5f2 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/SortBuilders/SortBuilderExtensions.cs @@ -0,0 +1,69 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Sources.Queries.SortBuilders; + +/// +/// Extensions for sort builders. +/// +public static class SortBuilderExtensions +{ + /// + /// 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 + /// . + /// + public static ISourceDataSortBuilder Reverse(this ISourceDataSortBuilder builder) + { + return new ReverseSourceDataSortBuilder { SourceDataSortBuilder = builder }; + } + + /// + /// 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 + /// . + /// + public static IMessageDataSortBuilder Reverse(this IMessageDataSortBuilder builder) + { + return new ReverseMessageDataSortBuilder { MessageDataSortBuilder = builder }; + } + + /// + /// 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 + /// . + /// + public static ILeaseDataSortBuilder Reverse(this ILeaseDataSortBuilder builder) + { + return new ReverseLeaseDataSortBuilder { LeaseDataSortBuilder = builder }; + } + + /// + /// 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 + /// . + /// + public static ITagDataSortBuilder Reverse(this ITagDataSortBuilder builder) + { + return new ReverseTagDataSortBuilder { TagDataSortBuilder = builder }; + } +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.cs new file mode 100644 index 00000000..3ab29f91 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteLeasesDataQuery.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 DeleteLeasesDataQuery(IReadOnlyCollection Leases, + object? Options = null) : ILeaseDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder 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(ILeaseDataSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs new file mode 100644 index 00000000..9f0e9737 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/DeleteTagsDataQuery.cs @@ -0,0 +1,37 @@ +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; + +namespace EntityDb.Common.Sources.Queries.Standard; + +internal sealed record DeleteTagsDataQuery + (Id StateId, IReadOnlyCollection Tags, object? Options = null) : ITagDataQuery +{ + public TFilter GetFilter(ITagDataFilterBuilder builder) + { + return builder.And + ( + builder.StateIdIn(StateId), + builder.Or + ( + Tags + .Select(deleteTag => builder.And( + builder.TagLabelEq(deleteTag.Label), + builder.TagValueEq(deleteTag.Value) + )) + .ToArray() + ) + ); + } + + public TSort? GetSort(ITagDataSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs new file mode 100644 index 00000000..d34344ba --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetDeltasDataQuery.cs @@ -0,0 +1,37 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Sources.Queries.Standard; + +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(KnownStateVersion.Next()), + }; + + if (StatePointer.StateVersion != StateVersion.Zero) + { + filters.Add(builder.StateVersionLte(StatePointer.StateVersion)); + } + + return builder.And(filters.ToArray()); + } + + public TSort GetSort(IMessageDataSortBuilder builder) + { + return builder.Combine + ( + builder.StateVersion(true) + ); + } + + public int? Skip => null; + + public int? Take => null; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs new file mode 100644 index 00000000..f59146ed --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetLastStateVersionDataQuery.cs @@ -0,0 +1,23 @@ +using EntityDb.Abstractions; +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 GetLastStateVersionDataQuery(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/GetSourceDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs new file mode 100644 index 00000000..34990e54 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/GetSourceDataQuery.cs @@ -0,0 +1,43 @@ +using EntityDb.Abstractions; +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 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); + } + + public TSort? GetSort(IMessageDataSortBuilder builder) + { + return default; + } + + public TFilter GetFilter(ISourceDataFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + 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/MatchingLeaseDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.cs new file mode 100644 index 00000000..0fb57dc0 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeaseDataQuery.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 MatchingLeaseDataQuery(ILease Lease) : ILeaseDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.And + ( + builder.LeaseScopeEq(Lease.Scope), + builder.LeaseLabelEq(Lease.Label), + builder.LeaseValueEq(Lease.Value) + ); + } + + public TSort? GetSort(ILeaseDataSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; + + public object? Options => null; +} diff --git a/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.cs new file mode 100644 index 00000000..3d2d6be7 --- /dev/null +++ b/src/EntityDb.Common/Sources/Queries/Standard/MatchingLeasesDataQuery.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 MatchingLeasesDataQuery(params ILease[] Leases) : ILeaseDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder 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(ILeaseDataSortBuilder builder) + { + return default; + } + + public int? Skip => null; + + public int? Take => null; + + public object? Options => null; +} diff --git a/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs new file mode 100644 index 00000000..0649b60b --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/BufferBlockSourceReprocessorQueue.cs @@ -0,0 +1,75 @@ +using EntityDb.Abstractions.Sources; +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.Create(item.SourceSessionOptionsName, + cancellationToken); + + var sourceIds = await sourceRepository + .EnumerateSourceIds(item.DataQuery, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var sourceId in sourceIds) + { + await Task.Delay(item.EnqueueDelay, cancellationToken); + + var source = await sourceRepository + .GetSource(sourceId, default, 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/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..92be236e --- /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. + /// + IDataQuery DataQuery { get; } + + /// + /// Determines how long to wait between each call to enqueue. + /// + TimeSpan EnqueueDelay { 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..a305f8c0 --- /dev/null +++ b/src/EntityDb.Common/Sources/ReprocessorQueues/TestModeSourceReprocessorQueue.cs @@ -0,0 +1,58 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Sources.Processors.Queues; +using Microsoft.Extensions.Logging; + +namespace EntityDb.Common.Sources.ReprocessorQueues; + +internal sealed 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.Create(item.SourceSessionOptionsName, + cancellationToken); + + var sourceIds = await sourceRepository + .EnumerateSourceIds(item.DataQuery, cancellationToken) + .ToArrayAsync(cancellationToken); + + foreach (var sourceId in sourceIds) + { + var source = await sourceRepository + .GetSource(sourceId, default, 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/SourceRepositoryExtensions.cs b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs new file mode 100644 index 00000000..ec5f8429 --- /dev/null +++ b/src/EntityDb.Common/Sources/SourceRepositoryExtensions.cs @@ -0,0 +1,92 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Common.Sources.Queries.Standard; + +namespace EntityDb.Common.Sources; + +/// +/// 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, + IDataQuery dataQuery, CancellationToken cancellationToken = default) + { + return dataQuery switch + { + ISourceDataQuery sourceDataQuery => sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken), + IMessageDataQuery messageDataQuery => sourceRepository.EnumerateSourceIds(messageDataQuery, + cancellationToken), + ILeaseDataQuery leaseDataQuery => sourceRepository.EnumerateSourceIds(leaseDataQuery, cancellationToken), + ITagDataQuery tagDataQuery => sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken), + _ => AsyncEnumerable.Empty(), + }; + } + + /// + /// Reconstruct the object by source id + /// + /// The source repository + /// The source id + /// Optional: The state 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, + Id? stateId = default, + CancellationToken cancellationToken = default + ) + { + var query = new GetSourceDataQuery(sourceId, stateId); + + var annotatedAgentSignature = await sourceRepository + .EnumerateAnnotatedAgentSignatures(query, cancellationToken) + .SingleAsync(cancellationToken); + + var messages = await sourceRepository + .EnumerateAnnotatedDeltas(query, cancellationToken) + .Select(annotatedDelta => new Message + { + Id = annotatedDelta.MessageId, + StatePointer = annotatedDelta.StatePointer, + Delta = annotatedDelta.Data, + }) + .ToArrayAsync(cancellationToken); + + return new Source + { + Id = annotatedAgentSignature.SourceId, + TimeStamp = annotatedAgentSignature.SourceTimeStamp, + AgentSignature = annotatedAgentSignature.Data, + Messages = messages.ToArray(), + }; + } +} diff --git a/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs new file mode 100644 index 00000000..474dc0a6 --- /dev/null +++ b/src/EntityDb.Common/Sources/SourceRepositoryWrapper.cs @@ -0,0 +1,126 @@ +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; +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(ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(sourceDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(messageDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateSourceIds(ILeaseDataQuery leaseDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(leaseDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateSourceIds(tagDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(sourceDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(messageDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(leaseDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateStatePointers(tagDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateAgentSignatures(sourceDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateDeltas(messageDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateLeases(ILeaseDataQuery leaseDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateLeases(leaseDataQuery, cancellationToken)); + } + + public IAsyncEnumerable EnumerateTags(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateTags(tagDataQuery, cancellationToken)); + } + + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => + _sourceRepository.EnumerateAnnotatedAgentSignatures(sourceDataQuery, cancellationToken)); + } + + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _sourceRepository.EnumerateAnnotatedDeltas(messageDataQuery, cancellationToken)); + } + + public virtual Task Commit(Source source, + CancellationToken cancellationToken = default) + { + return WrapCommand(() => _sourceRepository.Commit(source, cancellationToken)); + } + + public override async ValueTask DisposeAsync() + { + await _sourceRepository.DisposeAsync(); + } + + protected virtual IAsyncEnumerable WrapQuery(Func> enumerable) + { + return enumerable.Invoke(); + } + + protected virtual Task WrapCommand(Func> task) + { + return task.Invoke(); + } +} diff --git a/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs new file mode 100644 index 00000000..b9e4b9c7 --- /dev/null +++ b/src/EntityDb.Common/Sources/Subscribers/EntityStateSourceSubscriber.cs @@ -0,0 +1,27 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.Sources.Processors.Queues; + +namespace EntityDb.Common.Sources.Subscribers; + +internal sealed class EntityStateSourceSubscriber : ISourceSubscriber + where TEntity : IEntity +{ + private readonly ISourceProcessorQueue _sourceProcessorQueue; + + public EntityStateSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) + { + _sourceProcessorQueue = sourceProcessorQueue; + } + + public void Notify(Source source) + { + if (!source.Messages.Any(message => TEntity.CanReduce(message.Delta))) + { + return; + } + + _sourceProcessorQueue.Enqueue>(source); + } +} diff --git a/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs new file mode 100644 index 00000000..ae5468c4 --- /dev/null +++ b/src/EntityDb.Common/Sources/Subscribers/ProjectionStateSourceSubscriber.cs @@ -0,0 +1,27 @@ +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Sources.Processors; +using EntityDb.Common.Sources.Processors.Queues; + +namespace EntityDb.Common.Sources.Subscribers; + +internal sealed class ProjectionStateSourceSubscriber : ISourceSubscriber + where TProjection : IProjection +{ + private readonly ISourceProcessorQueue _sourceProcessorQueue; + + public ProjectionStateSourceSubscriber(ISourceProcessorQueue sourceProcessorQueue) + { + _sourceProcessorQueue = sourceProcessorQueue; + } + + public void Notify(Source source) + { + if (!TProjection.EnumerateProjectionIds(source).Any()) + { + return; + } + + _sourceProcessorQueue.Enqueue>(source); + } +} 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/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..e434c860 --- /dev/null +++ b/src/EntityDb.Common/States/StateRepositoryWrapper.cs @@ -0,0 +1,44 @@ +using EntityDb.Abstractions.States; +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(StatePointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + return WrapCommand(() => _stateRepository.Put(statePointer, state, cancellationToken)); + } + + public virtual Task Get(StatePointer statePointer, + CancellationToken cancellationToken = default) + { + return WrapQuery(() => _stateRepository.Get(statePointer, cancellationToken)); + } + + public virtual Task Delete(StatePointer[] 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..19b19a67 --- /dev/null +++ b/src/EntityDb.Common/States/TestModeStateManager.cs @@ -0,0 +1,54 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.States; + +internal sealed 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, StatePointer 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..6705a5f4 --- /dev/null +++ b/src/EntityDb.Common/States/TestModeStateRepository.cs @@ -0,0 +1,46 @@ +using EntityDb.Abstractions.States; +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(StatePointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + _testModeStateManager.AddStatePointer(this, statePointer); + + return _stateRepository.Put(statePointer, state, cancellationToken); + } + + public Task Get(StatePointer statePointer, CancellationToken cancellationToken = default) + { + return _stateRepository.Get(statePointer, cancellationToken); + } + + public Task Delete(StatePointer[] 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..4f7b0de4 --- /dev/null +++ b/src/EntityDb.Common/Streams/MultipleStreamRepository.cs @@ -0,0 +1,159 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.States.Deltas; +using EntityDb.Abstractions.Streams; +using EntityDb.Common.Disposables; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Queries.Standard; + +namespace EntityDb.Common.Streams; + +internal sealed class MultipleStreamRepository : DisposableResourceBaseClass, IMultipleStreamRepository +{ + private readonly IAgentAccessor _agentAccessor; + private readonly string _agentSignatureOptionsName; + private readonly Dictionary _knownStreams = new(); + private readonly List _messages = new(); + + public MultipleStreamRepository(IAgentAccessor agentAccessor, string agentSignatureOptionsName, + ISourceRepository sourceRepository) + { + _agentSignatureOptionsName = agentSignatureOptionsName; + _agentAccessor = agentAccessor; + + SourceRepository = sourceRepository; + } + + public ISourceRepository SourceRepository { get; } + + public async Task LoadOrCreate(IStateKey streamKey, CancellationToken cancellationToken = default) + { + if (_knownStreams.ContainsKey(streamKey)) + { + throw new ExistingStreamException(); + } + + var streamKeyLease = streamKey.ToLease(); + + var streamPointer = await GetStreamPointer(streamKeyLease, cancellationToken); + + var isNew = streamPointer == default; + + _knownStreams.Add(streamKey, + new Stream { Key = streamKey, Id = isNew ? Id.NewId() : streamPointer.Id, IsNew = isNew }); + } + + public async Task Load(IStateKey streamKey, CancellationToken cancellationToken = default) + { + if (_knownStreams.ContainsKey(streamKey)) + { + throw new ExistingStreamException(); + } + + var streamKeyLease = streamKey.ToLease(); + + var streamPointer = await GetStreamPointer(streamKeyLease, cancellationToken); + + if (streamPointer == default) + { + throw new UnknownStreamKeyException(); + } + + _knownStreams.Add(streamKey, new Stream { Key = streamKey, Id = streamPointer.Id, IsNew = false }); + } + + public void Create(IStateKey streamKey) + { + if (_knownStreams.ContainsKey(streamKey)) + { + throw new ExistingStreamException(); + } + + _knownStreams.Add(streamKey, new Stream { Key = streamKey, Id = Id.NewId(), IsNew = true }); + } + + public void Append(IStateKey streamKey, object delta) + { + if (!_knownStreams.TryGetValue(streamKey, out var stream)) + { + throw new UnknownStreamKeyException(); + } + + var nextStreamPointer = stream.GetNextPointer(); + + _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(); + } + + if (delta.GetMessageKey() is { } messageKey) + { + var messageKeyLease = messageKey.ToLease(streamKey); + + var streamPointer = await GetStreamPointer(messageKeyLease, cancellationToken); + + if (streamPointer != default) + { + return false; + } + } + + var nextStreamPointer = stream.GetNextPointer(); + + _messages.Add(Message.NewMessage(stream, nextStreamPointer, delta, streamKey)); + + return true; + } + + public async Task Commit(CancellationToken cancellationToken = default) + { + if (_messages.Count == 0) + { + return true; + } + + var agent = await _agentAccessor.GetAgent(_agentSignatureOptionsName, cancellationToken); + + 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(); + } + + private async Task GetStreamPointer(ILease lease, CancellationToken cancellationToken) + { + var query = new MatchingLeaseDataQuery(lease); + + return await SourceRepository + .EnumerateStatePointers(query, cancellationToken) + .SingleOrDefaultAsync(cancellationToken); + } +} diff --git a/src/EntityDb.Common/Streams/SingleStreamRepository.cs b/src/EntityDb.Common/Streams/SingleStreamRepository.cs new file mode 100644 index 00000000..7ce58e8a --- /dev/null +++ b/src/EntityDb.Common/Streams/SingleStreamRepository.cs @@ -0,0 +1,43 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; +using EntityDb.Abstractions.Streams; +using EntityDb.Common.Disposables; + +namespace EntityDb.Common.Streams; + +internal sealed class SingleStreamRepository : DisposableResourceBaseClass, ISingleStreamRepository +{ + private readonly IMultipleStreamRepository _multipleStreamRepository; + + public SingleStreamRepository(IMultipleStreamRepository multipleStreamRepository, IStateKey streamKey) + { + _multipleStreamRepository = multipleStreamRepository; + + StreamKey = streamKey; + } + + public ISourceRepository SourceRepository => _multipleStreamRepository.SourceRepository; + public IStateKey StreamKey { get; } + + 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); + } + + 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..b6894240 --- /dev/null +++ b/src/EntityDb.Common/Streams/Stream.cs @@ -0,0 +1,25 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.Streams; + +namespace EntityDb.Common.Streams; + +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; + } +} diff --git a/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs new file mode 100644 index 00000000..a1321bb3 --- /dev/null +++ b/src/EntityDb.Common/Streams/StreamRepositoryFactory.cs @@ -0,0 +1,88 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Agents; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Streams; + +namespace EntityDb.Common.Streams; + +internal sealed class StreamRepositoryFactory : IStreamRepositoryFactory +{ + private readonly IAgentAccessor _agentAccessor; + private readonly ISourceRepositoryFactory _sourceRepositoryFactory; + + public StreamRepositoryFactory + ( + IAgentAccessor agentAccessor, + ISourceRepositoryFactory sourceRepositoryFactory + ) + { + _agentAccessor = agentAccessor; + _sourceRepositoryFactory = sourceRepositoryFactory; + } + + public async Task CreateSingle + ( + IStateKey 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 CreateSingleForNew + ( + IStateKey streamKey, + string agentSignatureOptionsName, + string sourceSessionOptionsName, + CancellationToken cancellationToken = default + ) + { + var multipleStreamRepository = + await CreateMultiple(agentSignatureOptionsName, sourceSessionOptionsName, cancellationToken); + + multipleStreamRepository.Create(streamKey); + + return new SingleStreamRepository(multipleStreamRepository, streamKey); + } + + public async Task CreateSingleForExisting + ( + IStateKey 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, + string sourceSessionOptionsName, + CancellationToken cancellationToken + ) + { + var sourceRepository = await _sourceRepositoryFactory + .Create(sourceSessionOptionsName, cancellationToken); + + return new MultipleStreamRepository + ( + _agentAccessor, + agentSignatureOptionsName, + 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 2732246a..00000000 --- a/src/EntityDb.Common/Transactions/Builders/SingleEntityTransactionBuilder.cs +++ /dev/null @@ -1,79 +0,0 @@ -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; - -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 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 deleted file mode 100644 index f8be43df..00000000 --- a/src/EntityDb.Common/Transactions/Builders/TransactionBuilder.cs +++ /dev/null @@ -1,212 +0,0 @@ -using EntityDb.Abstractions.Agents; -using EntityDb.Abstractions.Leases; -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.Common.Entities; -using EntityDb.Common.Exceptions; -using EntityDb.Common.Transactions.Steps; -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 _transactionSteps = 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 previousEntity = _knownEntities[entityId]; - var previousEntityVersionNumber = previousEntity.GetVersionNumber(); - - var nextEntity = previousEntity.Reduce(command); - var nextEntityVersionNumber = nextEntity.GetVersionNumber(); - - _transactionSteps.Add(new AppendCommandTransactionStep - { - EntityId = entityId, - Entity = nextEntity, - EntityVersionNumber = nextEntityVersionNumber, - 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()); - - return this; - } - - public ITransaction Build(Id transactionId) - { - var transaction = new Transaction - { - Id = transactionId, - TimeStamp = _agent.TimeStamp, - AgentSignature = _agent.Signature, - Steps = _transactionSteps.ToImmutableArray() - }; - - _transactionSteps.Clear(); - - return transaction; - } - - private void ConstructIfNotKnown(Id entityId) - { - if (IsEntityKnown(entityId)) - { - return; - } - - var entity = TEntity.Construct(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/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/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/ProcessorQueues/BufferBlockTransactionQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionQueue.cs deleted file mode 100644 index 662ccc10..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/BufferBlockTransactionQueue.cs +++ /dev/null @@ -1,45 +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 BufferBlockTransactionQueue : BackgroundService, ITransactionProcessorQueue - where TTransactionProcessor : ITransactionProcessor -{ - private readonly BufferBlock _transactionQueue = new(); - private readonly ILogger> _logger; - private readonly TTransactionProcessor _transactionProcessor; - - public BufferBlockTransactionQueue(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)) - { - try - { - var transaction = await _transactionQueue.ReceiveAsync(stoppingToken); - - await _transactionProcessor.ProcessTransaction(transaction, stoppingToken); - } - 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/TestModeTransactionQueue.cs b/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionQueue.cs deleted file mode 100644 index 24ce7a94..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/ProcessorQueues/TestModeTransactionQueue.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 TestModeTransactionQueue : ITransactionProcessorQueue - where TTransactionProcessor : ITransactionProcessor -{ - private readonly TTransactionProcessor _transactionProcessor; - - public TestModeTransactionQueue(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/EntitySnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/EntitySnapshotTransactionProcessor.cs deleted file mode 100644 index b1016cde..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 - ) - { - _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 snapshotTransactionStepProcessorCache = new SnapshotTransactionStepProcessorCache(); - - var snapshotStepProcessor = new EntitySnapshotTransactionStepProcessor - ( - entityRepository, - snapshotTransactionStepProcessorCache - ); - - await ProcessTransactionSteps - ( - entityRepository.SnapshotRepository, - snapshotTransactionStepProcessorCache, - transaction, - snapshotStepProcessor, - 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/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/ITransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs deleted file mode 100644 index 09cffee9..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ITransactionProcessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using EntityDb.Abstractions.Transactions; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -/// -/// Represents a type that processes transactions emitted to a . -/// -public interface ITransactionProcessor -{ - /// - /// 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/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/ProjectionSnapshotTransactionProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionProcessor.cs deleted file mode 100644 index 0912fec2..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 - ) - { - _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 snapshotTransactionStepProcessorCache = new SnapshotTransactionStepProcessorCache(); - - var projectionSnapshotTransactionStepProcessor = new ProjectionSnapshotTransactionStepProcessor - ( - projectionRepository, - snapshotTransactionStepProcessorCache - ); - - await ProcessTransactionSteps - ( - projectionRepository.SnapshotRepository, - snapshotTransactionStepProcessorCache, - transaction, - projectionSnapshotTransactionStepProcessor, - 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/ProjectionSnapshotTransactionStepProcessor.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionStepProcessor.cs deleted file mode 100644 index e433c6d8..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/ProjectionSnapshotTransactionStepProcessor.cs +++ /dev/null @@ -1,70 +0,0 @@ -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 - where TProjection : IProjection -{ - private readonly IProjectionRepository _projectionRepository; - private readonly SnapshotTransactionStepProcessorCache _snapshotTransactionStepProcessorCache; - - public ProjectionSnapshotTransactionStepProcessor - ( - IProjectionRepository projectionRepository, - SnapshotTransactionStepProcessorCache snapshotTransactionStepProcessorCache - ) - { - _projectionRepository = projectionRepository; - _snapshotTransactionStepProcessorCache = snapshotTransactionStepProcessorCache; - } - - public async Task<(TProjection?, TProjection)?> GetSnapshots - ( - ITransaction transaction, - ITransactionStep transactionStep, - CancellationToken cancellationToken - ) - { - if (transactionStep is not IAppendCommandTransactionStep appendCommandTransactionStep) - { - return null; - } - - var projectionId = _projectionRepository.GetProjectionIdOrDefault(appendCommandTransactionStep.Entity); - - if (projectionId is null) - { - return null; - } - - var previousLatestPointer = projectionId.Value + appendCommandTransactionStep.PreviousEntityVersionNumber; - - TProjection? previousLatestSnapshot = default; - - if (previousLatestPointer.VersionNumber != VersionNumber.MinValue) - { - previousLatestSnapshot = _snapshotTransactionStepProcessorCache.GetSnapshotOrDefault(previousLatestPointer) ?? - await _projectionRepository.GetSnapshot(previousLatestPointer, - cancellationToken); - } - - var annotatedCommand = EntityAnnotation.CreateFromBoxedData - ( - transaction.Id, - transaction.TimeStamp, - appendCommandTransactionStep.EntityId, - appendCommandTransactionStep.EntityVersionNumber, - appendCommandTransactionStep.Command - ); - - var nextSnapshot = - (previousLatestSnapshot ?? TProjection.Construct(projectionId.Value)).Reduce(annotatedCommand); - - return (previousLatestSnapshot, nextSnapshot); - } -} 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 b08dc096..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionProcessorBase.cs +++ /dev/null @@ -1,57 +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 abstract Task ProcessTransaction(ITransaction transaction, CancellationToken cancellationToken); - - protected static async Task ProcessTransactionSteps - ( - ISnapshotRepository snapshotRepository, - SnapshotTransactionStepProcessorCache snapshotTransactionStepProcessorCache, - ITransaction transaction, - ISnapshotTransactionStepProcessor snapshotTransactionStepProcessor, - CancellationToken cancellationToken - ) - { - var putQueue = new Dictionary(); - - foreach (var transactionStep in transaction.Steps) - { - var snapshots = await snapshotTransactionStepProcessor.GetSnapshots(transaction, transactionStep, cancellationToken); - - if (snapshots is not var (previousLatestSnapshot, nextSnapshot)) - { - continue; - } - - var snapshotId = nextSnapshot.GetId(); - - if (nextSnapshot.ShouldRecordAsLatest(previousLatestSnapshot)) - { - putQueue[snapshotId] = nextSnapshot; - } - else - { - snapshotTransactionStepProcessorCache.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/Processors/SnapshotTransactionStepProcessorCache.cs b/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionStepProcessorCache.cs deleted file mode 100644 index c1b9d051..00000000 --- a/src/EntityDb.Common/Transactions/Subscribers/Processors/SnapshotTransactionStepProcessorCache.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.Abstractions.ValueObjects; - -namespace EntityDb.Common.Transactions.Subscribers.Processors; - -internal class SnapshotTransactionStepProcessorCache -{ - 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/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); - } -} diff --git a/src/EntityDb.Common/Transactions/Transaction.cs b/src/EntityDb.Common/Transactions/Transaction.cs deleted file mode 100644 index 669116ca..00000000 --- a/src/EntityDb.Common/Transactions/Transaction.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.Transactions.Steps; -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 Steps { 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..e6a44905 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolver.cs @@ -5,25 +5,16 @@ namespace EntityDb.Common.TypeResolvers; -internal class DefaultPartialTypeResolver : IPartialTypeResolver +internal sealed class DefaultPartialTypeResolver : IPartialTypeResolver { private readonly IOptions _options; + private readonly Dictionary _cache = new(); public DefaultPartialTypeResolver(IOptions options) { _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) || @@ -34,15 +25,59 @@ public bool TryResolveType(EnvelopeHeaders envelopeHeaders, [NotNullWhen(true)] return 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 bool TryGetType(string qualifiedTypeName, [NotNullWhen(true)] out Type? resolvedType) + { + foreach (var (oldNamespace, newNamespace) in _options.Value.UpdateNamespaces) + { + qualifiedTypeName = qualifiedTypeName.Replace(oldNamespace, newNamespace); + } + resolvedType = Type.GetType ( - Assembly.CreateQualifiedName(assemblyFullName, typeFullName), + qualifiedTypeName, AssemblyResolver, - (assembly, typeName, ignoreCase) => assembly!.GetType(typeName, true, ignoreCase), - true, - false - )!; + TypeResolver, + _options.Value.ThrowOnError, + _options.Value.IgnoreCase + ); - return true; + return resolvedType is not null; + } + + private Assembly AssemblyResolver(AssemblyName assemblyName) + { + if (_options.Value.IgnoreVersion) + { + assemblyName.Version = null; + } + + 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 04e95392..80ba20a1 100644 --- a/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs +++ b/src/EntityDb.Common/TypeResolvers/DefaultPartialTypeResolverOptions.cs @@ -3,8 +3,25 @@ namespace EntityDb.Common.TypeResolvers; /// /// Options for the default partial type resolver /// -public class DefaultPartialTypeResolverOptions +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 UpdateNamespaces { get; set; } = new(); + /// /// If you version your assemblies, you may want to ignore the /// version of the assembly for type resolving purposes. 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 441fcdc5..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, null) - { - } -} diff --git a/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs b/src/EntityDb.EntityFramework/Converters/TimeStampConverter.cs deleted file mode 100644 index e5664e25..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, null) - { - } -} diff --git a/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs b/src/EntityDb.EntityFramework/Converters/VersionNumberConverter.cs deleted file mode 100644 index 84ef7145..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, null) - { - } -} 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 ca8d5471..00000000 --- a/src/EntityDb.EntityFramework/DbContexts/EntityDbContextFactory.cs +++ /dev/null @@ -1,26 +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 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 deleted file mode 100644 index 4661b7a2..00000000 --- a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContext.cs +++ /dev/null @@ -1,19 +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 . - /// - /// The options for the database - /// A new that will be configured using . - static abstract TDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions); -} diff --git a/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs b/src/EntityDb.EntityFramework/DbContexts/IEntityDbContextFactory.cs deleted file mode 100644 index 4fba5f01..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 2ec2f457..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 b5ff49a8..00000000 --- a/src/EntityDb.EntityFramework/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Extensions; -using EntityDb.EntityFramework.DbContexts; -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, 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 629e685b..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 entityFrameworkSnapshotRepositoyFactory, - IServiceProvider serviceProvider, - bool testMode) - { - return testMode - ? TestModeEntityFrameworkSnapshotRepositoryFactory.Create(serviceProvider, entityFrameworkSnapshotRepositoyFactory) - : entityFrameworkSnapshotRepositoyFactory; - } -} diff --git a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs b/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs deleted file mode 100644 index 244d0de0..00000000 --- a/src/EntityDb.EntityFramework/Predicates/PredicateBuilder.cs +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index c5d64f07..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 sapshotExists = await _snapshots - .Where(snapshot.GetKeyPredicate()) - .AnyAsync(cancellationToken); - - if (!sapshotExists) - { - _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 2d28768c..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 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. - /// - 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 d547183b..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 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 833b58b2..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 napshot 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 74b6287f..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.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 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 28b675b4..00000000 --- a/src/EntityDb.InMemory/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,35 +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.AddScoped(serviceProvider => ActivatorUtilities - .CreateInstance>(serviceProvider) - .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 394e0ee4..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 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 9c3c151e..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 snapshotPointer, TSnapshot snapshot, - CancellationToken cancellationToken = default) - { - return _inMemorySession.Insert(snapshotPointer, 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/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..1b16fa8a 100644 --- a/src/EntityDb.Json/Converters/IdConverter.cs +++ b/src/EntityDb.Json/Converters/IdConverter.cs @@ -1,10 +1,10 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; using System.Text.Json; using System.Text.Json.Serialization; namespace EntityDb.Json.Converters; -internal class IdConverter : JsonConverter +internal sealed class IdConverter : JsonConverter { public override Id Read ( 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..22e7a87f 100644 --- a/src/EntityDb.Json/Converters/VersionNumberConverter.cs +++ b/src/EntityDb.Json/Converters/VersionConverter.cs @@ -1,12 +1,12 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using System.Text.Json; using System.Text.Json.Serialization; namespace EntityDb.Json.Converters; -internal class VersionNumberConverter : JsonConverter +internal sealed class VersionConverter : JsonConverter { - public override VersionNumber Read + public override StateVersion Read ( ref Utf8JsonReader reader, Type typeToConvert, @@ -20,17 +20,17 @@ JsonSerializerOptions options var ulongValue = reader.GetUInt64(); - return new VersionNumber(ulongValue); + return new StateVersion(ulongValue); } public override void Write ( Utf8JsonWriter writer, - VersionNumber versionNumber, + StateVersion stateVersion, JsonSerializerOptions options ) { - var ulongValue = versionNumber.Value; + var ulongValue = stateVersion.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..1c44add4 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 @@ -59,7 +58,7 @@ public TSerializedData Serialize(TData data) { _logger.LogError(exception, "Unable to serialize"); - throw new SerializeException(); + throw new DataSerializationException(); } } @@ -76,7 +75,11 @@ public TData Deserialize(TSerializedData serializedData) { _logger.LogError(exception, "Unable to deserialize"); - throw new DeserializeException(); + throw new DataDeserializationException(); } } + + 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.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/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs deleted file mode 100644 index 0e9930fc..00000000 --- a/src/EntityDb.MongoDb/Commands/DeleteDocumentsCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EntityDb.MongoDb.Sessions; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Commands; - -internal record DeleteDocumentsCommand -( - string CollectionName, - FilterDefinition FilterDefinition -) -{ - public async Task Execute(IMongoSession mongoSession, CancellationToken cancellationToken) - { - await mongoSession - .Delete(CollectionName, FilterDefinition, cancellationToken); - } -} diff --git a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs index 811f79d3..e646c937 100644 --- a/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs +++ b/src/EntityDb.MongoDb/Documents/AgentSignatureDocument.cs @@ -1,42 +1,51 @@ -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 : SourceDataDocumentBase { 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 SourceDataSortBuilder DataSortBuilder = new(); public static InsertDocumentsCommand GetInsertCommand ( IEnvelopeService envelopeService, - ITransaction transaction + Source source ) { + var messageIds = source.Messages + .Select(message => message.Id) + .ToArray(); + + var statePointers = source.Messages + .Select(message => message.StatePointer) + .Distinct() + .ToArray(); + + var stateIds = statePointers + .Select(statePointer => statePointer.Id) + .ToArray(); + var documents = new[] { new AgentSignatureDocument { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityIds = transaction.Steps.Select(transactionStep => transactionStep.EntityId).Distinct() - .ToArray(), - DataType = transaction.AgentSignature.GetType().Name, - Data = envelopeService.Serialize(transaction.AgentSignature) - } + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + MessageIds = messageIds, + StateIds = stateIds, + StatePointers = statePointers, + DataType = source.AgentSignature.GetType().Name, + Data = envelopeService.Serialize(source.AgentSignature), + }, }; return new InsertDocumentsCommand @@ -48,17 +57,17 @@ ITransaction transaction public static DocumentQuery GetQuery ( - IAgentSignatureQuery agentSignatureQuery + ISourceDataQuery sourceDataQuery ) { return new DocumentQuery - ( - CollectionName, - agentSignatureQuery.GetFilter(FilterBuilder), - agentSignatureQuery.GetSort(SortBuilder), - agentSignatureQuery.Skip, - agentSignatureQuery.Take, - agentSignatureQuery.Options as MongoDbQueryOptions - ); + { + CollectionName = CollectionName, + 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/CommandDocument.cs b/src/EntityDb.MongoDb/Documents/CommandDocument.cs deleted file mode 100644 index 2be01095..00000000 --- a/src/EntityDb.MongoDb/Documents/CommandDocument.cs +++ /dev/null @@ -1,90 +0,0 @@ -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; -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.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, - IAppendCommandTransactionStep appendCommandTransactionStep - ) - { - var documents = new[] - { - new CommandDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = appendCommandTransactionStep.EntityId, - EntityVersionNumber = appendCommandTransactionStep.EntityVersionNumber, - DataType = appendCommandTransactionStep.Command.GetType().Name, - Data = envelopeService.Serialize(appendCommandTransactionStep.Command) - } - }; - - 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 is null - ? default - : document.EntityVersionNumber; - } -} diff --git a/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs new file mode 100644 index 00000000..54ef6771 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/Commands/DeleteDocumentsCommand.cs @@ -0,0 +1,17 @@ +using EntityDb.MongoDb.Sources.Sessions; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Documents.Commands; + +internal sealed record DeleteDocumentsCommand +{ + public required string CollectionName { get; init; } + public required FilterDefinition FilterDefinition { get; init; } + + public async Task Execute(IMongoSession mongoSession, CancellationToken cancellationToken) + { + await mongoSession + .Delete(CollectionName, FilterDefinition, cancellationToken); + } +} diff --git a/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs b/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs similarity index 61% rename from src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs rename to src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs index 7494c333..4a5467ea 100644 --- a/src/EntityDb.MongoDb/Commands/InsertDocumentsCommand.cs +++ b/src/EntityDb.MongoDb/Documents/Commands/InsertDocumentsCommand.cs @@ -1,8 +1,8 @@ -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Sources.Sessions; -namespace EntityDb.MongoDb.Commands; +namespace EntityDb.MongoDb.Documents.Commands; -internal record InsertDocumentsCommand +internal sealed record InsertDocumentsCommand ( string CollectionName, TDocument[] Documents diff --git a/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs new file mode 100644 index 00000000..b6dbf3ca --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/DeltaDataDocument.cs @@ -0,0 +1,93 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; +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; + +namespace EntityDb.MongoDb.Documents; + +internal sealed record DeltaDataDocument : MessageDataDocumentBase +{ + public const string CollectionName = "Deltas"; + + private static readonly ProjectionDefinition StateVersionProjection = + ProjectionBuilder.Combine + ( + ProjectionBuilder.Exclude(nameof(_id)), + ProjectionBuilder.Include(nameof(StateVersion)) + ); + + private static readonly MessageDataFilterBuilder DataFilterBuilder = new(); + + private static readonly MessageDataSortBuilder DataSortBuilder = new(); + + public static InsertDocumentsCommand GetInsertCommand + ( + IEnvelopeService envelopeService, + Source source, + Message message + ) + { + var documents = new[] + { + new DeltaDataDocument + { + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + MessageId = message.Id, + StateId = message.StatePointer.Id, + StateVersion = message.StatePointer.StateVersion, + StatePointer = message.StatePointer, + DataType = message.Delta.GetType().Name, + Data = envelopeService.Serialize(message.Delta), + }, + }; + + return new InsertDocumentsCommand + ( + CollectionName, + documents + ); + } + + public static DocumentQuery GetQuery + ( + IMessageDataQuery messageDataQuery + ) + { + return new DocumentQuery + { + CollectionName = CollectionName, + Filter = messageDataQuery.GetFilter(DataFilterBuilder), + Sort = messageDataQuery.GetSort(DataSortBuilder), + Skip = messageDataQuery.Skip, + Limit = messageDataQuery.Take, + Options = messageDataQuery.Options as MongoDbQueryOptions, + }; + } + + public static async Task GetLastStateVersion + ( + IMongoSession mongoSession, + Id stateId, + CancellationToken cancellationToken + ) + { + var lastStateVersionQuery = new GetLastStateVersionDataQuery(stateId); + + var document = await GetQuery(lastStateVersionQuery) + .Execute(mongoSession, StateVersionProjection, cancellationToken) + .SingleOrDefaultAsync(cancellationToken); + + return document?.StateVersion ?? default; + } +} diff --git a/src/EntityDb.MongoDb/Documents/DocumentBase.cs b/src/EntityDb.MongoDb/Documents/DocumentBase.cs index 506bdae3..5b0909be 100644 --- a/src/EntityDb.MongoDb/Documents/DocumentBase.cs +++ b/src/EntityDb.MongoDb/Documents/DocumentBase.cs @@ -1,15 +1,34 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +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 70% rename from src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs rename to src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs index 198f3a66..7c4b7305 100644 --- a/src/EntityDb.MongoDb/Envelopes/BsonDocumentEnvelopeService.cs +++ b/src/EntityDb.MongoDb/Documents/Envelopes/MongoDbEnvelopeService.cs @@ -1,15 +1,17 @@ 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 +internal sealed class MongoDbEnvelopeService : IEnvelopeService { public const string TypeDiscriminatorPropertyName = "_t"; @@ -19,7 +21,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 VersionSerializer()); } public MongoDbEnvelopeService @@ -55,7 +61,7 @@ public BsonDocument Serialize(TData data) { _logger.LogError(exception, "Unable to serialize"); - throw new SerializeException(); + throw new DataSerializationException(); } } @@ -63,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)); } @@ -71,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/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/LeaseDataDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs new file mode 100644 index 00000000..15baba20 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/LeaseDataDocument.cs @@ -0,0 +1,85 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +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 MongoDB.Bson; + +namespace EntityDb.MongoDb.Documents; + +internal sealed record LeaseDataDocument : MessageDataDocumentBase +{ + public const string CollectionName = "Leases"; + + private static readonly LeaseDataFilterBuilder DataFilterBuilder = new(); + + private static readonly LeaseDataSortBuilder DataSortBuilder = new(); + + public required string Scope { get; init; } + public required string Label { get; init; } + public required string Value { get; init; } + + public static InsertDocumentsCommand GetInsertCommand + ( + IEnvelopeService envelopeService, + Source source, + Message message + ) + { + var leaseDocuments = message.AddLeases + .Select(lease => new LeaseDataDocument + { + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + MessageId = message.Id, + StateId = message.StatePointer.Id, + StateVersion = message.StatePointer.StateVersion, + StatePointer = message.StatePointer, + DataType = lease.GetType().Name, + Data = envelopeService.Serialize(lease), + Scope = lease.Scope, + Label = lease.Label, + Value = lease.Value, + }) + .ToArray(); + + return new InsertDocumentsCommand + ( + CollectionName, + leaseDocuments + ); + } + + public static DocumentQuery GetQuery + ( + ILeaseDataQuery leaseDataQuery + ) + { + return new DocumentQuery + { + CollectionName = CollectionName, + Filter = leaseDataQuery.GetFilter(DataFilterBuilder), + Sort = leaseDataQuery.GetSort(DataSortBuilder), + Skip = leaseDataQuery.Skip, + Limit = leaseDataQuery.Take, + Options = leaseDataQuery.Options as MongoDbQueryOptions, + }; + } + + public static DeleteDocumentsCommand GetDeleteCommand + ( + Message message + ) + { + var deleteLeasesQuery = new DeleteLeasesDataQuery(message.DeleteLeases); + + return new DeleteDocumentsCommand + { + CollectionName = CollectionName, FilterDefinition = deleteLeasesQuery.GetFilter(DataFilterBuilder), + }; + } +} diff --git a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs b/src/EntityDb.MongoDb/Documents/LeaseDocument.cs deleted file mode 100644 index edc1f5a5..00000000 --- a/src/EntityDb.MongoDb/Documents/LeaseDocument.cs +++ /dev/null @@ -1,88 +0,0 @@ -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; -using EntityDb.MongoDb.Commands; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Queries.FilterBuilders; -using EntityDb.MongoDb.Queries.SortBuilders; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Documents; - -internal sealed record LeaseDocument : DocumentBase, IEntityDocument -{ - public const string CollectionName = "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 static InsertDocumentsCommand GetInsertCommand - ( - IEnvelopeService envelopeService, - ITransaction transaction, - IAddLeasesTransactionStep addLeasesTransactionStep - ) - { - var leaseDocuments = addLeasesTransactionStep.Leases - .Select(insertLease => new LeaseDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = addLeasesTransactionStep.EntityId, - EntityVersionNumber = addLeasesTransactionStep.EntityVersionNumber, - DataType = insertLease.GetType().Name, - Data = envelopeService.Serialize(insertLease), - Scope = insertLease.Scope, - Label = insertLease.Label, - Value = insertLease.Value - }) - .ToArray(); - - return new InsertDocumentsCommand - ( - CollectionName, - leaseDocuments - ); - } - - public static DocumentQuery GetQuery - ( - ILeaseQuery leaseQuery - ) - { - return new DocumentQuery - ( - CollectionName, - leaseQuery.GetFilter(FilterBuilder), - leaseQuery.GetSort(SortBuilder), - leaseQuery.Skip, - leaseQuery.Take, - leaseQuery.Options as MongoDbQueryOptions - ); - } - - public static DeleteDocumentsCommand GetDeleteCommand - ( - IDeleteLeasesTransactionStep deleteLeasesTransactionStep - ) - { - var deleteLeasesQuery = - new DeleteLeasesQuery(deleteLeasesTransactionStep.EntityId, deleteLeasesTransactionStep.Leases); - - return new DeleteDocumentsCommand - ( - CollectionName, - deleteLeasesQuery.GetFilter(FilterBuilder) - ); - } -} diff --git a/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs b/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs new file mode 100644 index 00000000..a2ef7abe --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/MessageDataDocumentBase.cs @@ -0,0 +1,19 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; +using EntityDb.Common.Sources.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +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 StateVersion StateVersion { get; init; } + + public required Id MessageId { get; init; } + public required StatePointer StatePointer { get; init; } +} diff --git a/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs new file mode 100644 index 00000000..87beef37 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQuery.cs @@ -0,0 +1,22 @@ +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Sessions; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Documents.Queries; + +internal sealed record DocumentQuery +{ + 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) + { + 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..b4b1998c --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/Queries/DocumentQueryExtensions.cs @@ -0,0 +1,153 @@ +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; +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 EnumerateSourceDataStatePointers + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + CancellationToken cancellationToken + ) + where TDocument : SourceDataDocumentBase + { + return documentQuery.EnumeratePointers + ( + mongoSession, + SourceDataDocumentBase.StatePointersProjection, + documents => documents.SelectMany(document => AsyncEnumerablePolyfill.FromResult(document.StatePointers)), + cancellationToken + ); + } + + public static IAsyncEnumerable EnumerateMessageStatePointers + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + CancellationToken cancellationToken + ) + where TDocument : MessageDataDocumentBase + { + return documentQuery.EnumeratePointers + ( + mongoSession, + MessageDataDocumentBase.StatePointerProjection, + documents => documents.Select(document => document.StatePointer), + 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> EnumerateAnnotatedSourceData + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + IEnvelopeService envelopeService, + CancellationToken cancellationToken + ) + where TDocument : MessageDataDocumentBase + where TData : notnull + { + var documents = documentQuery.Execute(mongoSession, DocumentBase.NoIdProjection, cancellationToken); + + return documents.EnumerateAnnotatedSourceData(envelopeService, + cancellationToken); + } + + public static IAsyncEnumerable> EnumerateEntitiesAnnotation + ( + this DocumentQuery documentQuery, + IMongoSession mongoSession, + IEnvelopeService envelopeService, + CancellationToken cancellationToken + ) + where TDocument : SourceDataDocumentBase + 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 92% rename from src/EntityDb.MongoDb/Serializers/EnvelopeSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs index 95a9104a..a6b1683c 100644 --- a/src/EntityDb.MongoDb/Serializers/EnvelopeSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/EnvelopeSerializer.cs @@ -4,9 +4,9 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -namespace EntityDb.MongoDb.Serializers; +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/Serializers/IdSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs similarity index 83% rename from src/EntityDb.MongoDb/Serializers/IdSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs index fb029335..1d693759 100644 --- a/src/EntityDb.MongoDb/Serializers/IdSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/IdSerializer.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions; using MongoDB.Bson.Serialization; -namespace EntityDb.MongoDb.Serializers; +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/Serializers/TimeStampSerializer.cs b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs similarity index 85% rename from src/EntityDb.MongoDb/Serializers/TimeStampSerializer.cs rename to src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs index def754e2..4c6efaaf 100644 --- a/src/EntityDb.MongoDb/Serializers/TimeStampSerializer.cs +++ b/src/EntityDb.MongoDb/Documents/Serializers/TimeStampSerializer.cs @@ -1,9 +1,9 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.Sources; using MongoDB.Bson.Serialization; -namespace EntityDb.MongoDb.Serializers; +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/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..c6733d80 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 EntityDb.Abstractions.States; using MongoDB.Bson.Serialization; -namespace EntityDb.MongoDb.Serializers; +namespace EntityDb.MongoDb.Documents.Serializers; -internal class VersionNumberSerializer : IBsonSerializer +internal sealed class VersionSerializer : IBsonSerializer { - public Type ValueType => typeof(VersionNumber); + public Type ValueType => typeof(StateVersion); object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { return Deserialize(context, args); } - public VersionNumber Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + public StateVersion Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var longValue = context.Reader.ReadInt64(); var ulongValue = Convert.ToUInt64(longValue); - return new VersionNumber(ulongValue); + return new StateVersion(ulongValue); } public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - if (value is not VersionNumber versionNumber) + if (value is not StateVersion 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, StateVersion stateVersion) { - var longValue = Convert.ToInt64(versionNumber.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 new file mode 100644 index 00000000..88692249 --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/SourceDataDocumentBase.cs @@ -0,0 +1,21 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; +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 DataFilterBuilder = new(); + + public static ProjectionDefinition StatePointersProjection { get; } = + ProjectionBuilder.Include(nameof(StatePointers)); + + public required Id[] StateIds { get; init; } + + public required Id[] MessageIds { get; init; } + public required StatePointer[] StatePointers { get; init; } +} diff --git a/src/EntityDb.MongoDb/Documents/StateDocument.cs b/src/EntityDb.MongoDb/Documents/StateDocument.cs new file mode 100644 index 00000000..66ea162d --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/StateDocument.cs @@ -0,0 +1,18 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace EntityDb.MongoDb.Documents; + +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 StateId { 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 new file mode 100644 index 00000000..4de8829f --- /dev/null +++ b/src/EntityDb.MongoDb/Documents/TagDataDocument.cs @@ -0,0 +1,83 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +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 MongoDB.Bson; + +namespace EntityDb.MongoDb.Documents; + +internal sealed record TagDataDocument : MessageDataDocumentBase +{ + public const string CollectionName = "Tags"; + + private static readonly TagDataFilterBuilder DataFilterBuilder = new(); + + private static readonly TagDataSortBuilder DataSortBuilder = new(); + + public required string Label { get; init; } + public required string Value { get; init; } + + public static InsertDocumentsCommand GetInsertCommand + ( + IEnvelopeService envelopeService, + Source source, + Message message + ) + { + var tagDocuments = message.AddTags + .Select(tag => new TagDataDocument + { + SourceTimeStamp = source.TimeStamp, + SourceId = source.Id, + MessageId = message.Id, + StateId = message.StatePointer.Id, + StateVersion = message.StatePointer.StateVersion, + StatePointer = message.StatePointer, + DataType = tag.GetType().Name, + Data = envelopeService.Serialize(tag), + Label = tag.Label, + Value = tag.Value, + }) + .ToArray(); + + return new InsertDocumentsCommand + ( + CollectionName, + tagDocuments + ); + } + + public static DocumentQuery GetQuery + ( + ITagDataQuery tagDataQuery + ) + { + return new DocumentQuery + { + CollectionName = CollectionName, + Filter = tagDataQuery.GetFilter(DataFilterBuilder), + Sort = tagDataQuery.GetSort(DataSortBuilder), + Skip = tagDataQuery.Skip, + Limit = tagDataQuery.Take, + Options = tagDataQuery.Options as MongoDbQueryOptions, + }; + } + + public static DeleteDocumentsCommand GetDeleteCommand + ( + Message message + ) + { + var deleteTagsQuery = new DeleteTagsDataQuery(message.StatePointer.Id, message.DeleteTags); + + return new DeleteDocumentsCommand + { + CollectionName = CollectionName, FilterDefinition = deleteTagsQuery.GetFilter(DataFilterBuilder), + }; + } +} diff --git a/src/EntityDb.MongoDb/Documents/TagDocument.cs b/src/EntityDb.MongoDb/Documents/TagDocument.cs deleted file mode 100644 index 181168b1..00000000 --- a/src/EntityDb.MongoDb/Documents/TagDocument.cs +++ /dev/null @@ -1,85 +0,0 @@ -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; -using EntityDb.MongoDb.Commands; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Queries.FilterBuilders; -using EntityDb.MongoDb.Queries.SortBuilders; -using MongoDB.Bson; - -namespace EntityDb.MongoDb.Documents; - -internal sealed record TagDocument : DocumentBase, IEntityDocument -{ - public const string CollectionName = "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 static InsertDocumentsCommand GetInsertCommand - ( - IEnvelopeService envelopeService, - ITransaction transaction, - IAddTagsTransactionStep addTagsTransactionStep - ) - { - var tagDocuments = addTagsTransactionStep.Tags - .Select(insertTag => new TagDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = addTagsTransactionStep.EntityId, - EntityVersionNumber = addTagsTransactionStep.EntityVersionNumber, - DataType = insertTag.GetType().Name, - Data = envelopeService.Serialize(insertTag), - Label = insertTag.Label, - Value = insertTag.Value - }) - .ToArray(); - - return new InsertDocumentsCommand - ( - CollectionName, - tagDocuments - ); - } - - public static DocumentQuery GetQuery - ( - ITagQuery tagQuery - ) - { - return new DocumentQuery - ( - CollectionName, - tagQuery.GetFilter(FilterBuilder), - tagQuery.GetSort(SortBuilder), - tagQuery.Skip, - tagQuery.Take, - tagQuery.Options as MongoDbQueryOptions - ); - } - - public static DeleteDocumentsCommand GetDeleteCommand - ( - IDeleteTagsTransactionStep deleteTagsTransactionStep - ) - { - var deleteTagsQuery = new DeleteTagsQuery(deleteTagsTransactionStep.EntityId, deleteTagsTransactionStep.Tags); - - return new DeleteDocumentsCommand - ( - CollectionName, - deleteTagsQuery.GetFilter(FilterBuilder) - ); - } -} diff --git a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj index fdde2e40..6365532d 100644 --- a/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj +++ b/src/EntityDb.MongoDb/EntityDb.MongoDb.csproj @@ -1,17 +1,16 @@  - - EntityDb EventSourcing DDD CQRS MongoDb - An implementation of the EntityDb Transaction Repository interface, specifically for MongoDb. + EntityDb EventSourcing EventStreaming DDD CQRS + Implementations of the EntityDb ISourceRepository and IStateRepository 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 6e6d8c47..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.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 d4c54cb6..2e07516e 100644 --- a/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/MongoClientExtensions.cs @@ -14,11 +14,17 @@ private static readonly IndexKeysDefinitionBuilder private static readonly CreateIndexOptions UniquenessConstraint = new() { - Name = "Uniqueness Constraint", - Unique = true + Name = "Uniqueness Constraint", Unique = true, }; - private static readonly Dictionary[]> Collections = new() + private static readonly CreateIndexOptions IdempotentConstraint = new() + { + Name = "Idempotent Constraint", Unique = true, + }; + + private static readonly CreateIndexOptions LookupIndex = new() { Name = "Lookup Index", Unique = false }; + + private static readonly Dictionary[]> SourceCollections = new() { [AgentSignatureDocument.CollectionName] = new[] { @@ -26,48 +32,68 @@ private static readonly IndexKeysDefinitionBuilder ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(AgentSignatureDocument.TransactionId)) + IndexKeysBuilder.Descending(nameof(AgentSignatureDocument.SourceId)) ), UniquenessConstraint - ) + ), }, - [CommandDocument.CollectionName] = new[] + [DeltaDataDocument.CollectionName] = new[] { new CreateIndexModel ( IndexKeysBuilder.Combine ( - IndexKeysBuilder.Descending(nameof(CommandDocument.EntityId)), - IndexKeysBuilder.Descending(nameof(CommandDocument.EntityVersionNumber)) + IndexKeysBuilder.Descending(nameof(DeltaDataDocument.MessageId)) + ), + IdempotentConstraint + ), + new CreateIndexModel + ( + IndexKeysBuilder.Combine + ( + 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)) ), - new CreateIndexOptions { Name = "Lookup Index" } - ) - } + LookupIndex + ), + }, + }; + + private static readonly CreateIndexModel[] StateCollection = + { + new( + IndexKeysBuilder.Combine + ( + IndexKeysBuilder.Descending(nameof(StateDocument.StateId)), + IndexKeysBuilder.Descending(nameof(StateDocument.StateVersion)) + ), + UniquenessConstraint + ), }; /// @@ -81,20 +107,22 @@ 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 ProvisionSourceCollections(this IMongoClient mongoClient, string serviceName, CancellationToken cancellationToken = default) { var mongoDatabase = mongoClient.GetDatabase(serviceName); - foreach (var (collectionName, collectionIndices) in Collections) + foreach (var (collectionName, collectionIndices) in SourceCollections) { 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); + + var sourceCollectionNames = await sourceCollectionNameCursor + .ToListAsync(cancellationToken); - if (entityCollectionNames.Contains(collectionName)) + if (sourceCollectionNames.Contains(collectionName)) { continue; } @@ -104,4 +132,42 @@ 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. + /// 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 ProvisionStateCollection(this IMongoClient mongoClient, string serviceName, + string collectionName, + CancellationToken cancellationToken = default) + { + var collectionIndices = StateCollection; + + var mongoDatabase = mongoClient.GetDatabase(serviceName); + + var mongoCollection = mongoDatabase.GetCollection(collectionName); + + var collectionNameCursor = await mongoDatabase + .ListCollectionNamesAsync(cancellationToken: cancellationToken); + + var collectionNames = await collectionNameCursor + .ToListAsync(cancellationToken); + + if (collectionNames.Contains(collectionName)) + { + return; + } + + await mongoDatabase.CreateCollectionAsync(collectionName, cancellationToken: cancellationToken); + + await mongoCollection.Indexes.CreateManyAsync(collectionIndices, cancellationToken); + } } diff --git a/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs b/src/EntityDb.MongoDb/Extensions/MongoDbTransactionRepositoryFactoryExtensions.cs deleted file mode 100644 index fdd2506a..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 UseAuthProvision( - 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 d6a28f28..ab9f5bed 100644 --- a/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -1,8 +1,9 @@ -using EntityDb.Abstractions.Transactions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; using EntityDb.Common.Extensions; -using EntityDb.MongoDb.Envelopes; -using EntityDb.MongoDb.Transactions; -using Microsoft.Extensions.Configuration; +using EntityDb.MongoDb.Documents.Envelopes; +using EntityDb.MongoDb.Sources; +using EntityDb.MongoDb.States; using Microsoft.Extensions.DependencyInjection; namespace EntityDb.MongoDb.Extensions; @@ -20,29 +21,57 @@ 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) - .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 AddMongoDbStateRepository(this IServiceCollection serviceCollection, + bool testMode = false, bool autoProvision = false) + where TState : 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/BuilderBase.cs b/src/EntityDb.MongoDb/Queries/BuilderBase.cs deleted file mode 100644 index ab134302..00000000 --- a/src/EntityDb.MongoDb/Queries/BuilderBase.cs +++ /dev/null @@ -1,14 +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}"; - - protected const string DataValueFieldName = - $"{nameof(DocumentBase.Data)}.{nameof(Envelope.Value)}"; -} diff --git a/src/EntityDb.MongoDb/Queries/DocumentQuery.cs b/src/EntityDb.MongoDb/Queries/DocumentQuery.cs deleted file mode 100644 index c1f76a19..00000000 --- a/src/EntityDb.MongoDb/Queries/DocumentQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using EntityDb.MongoDb.Sessions; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries; - -internal record DocumentQuery -( - string CollectionName, - FilterDefinition Filter, - SortDefinition? Sort, - int? Skip, - int? Limit, - MongoDbQueryOptions? Options -) -{ - 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/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs b/src/EntityDb.MongoDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs deleted file mode 100644 index 1f3aa150..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 EntityIdsIn(params Id[] entityIds) - { - return AnyIn(nameof(AgentSignatureDocument.EntityIds), entityIds); - } - - 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/SortBuilderBase.cs b/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs deleted file mode 100644 index 0a1a38f1..00000000 --- a/src/EntityDb.MongoDb/Queries/SortBuilders/SortBuilderBase.cs +++ /dev/null @@ -1,43 +0,0 @@ -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.MongoDb.Documents; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace EntityDb.MongoDb.Queries.SortBuilders; - -internal abstract class SortBuilderBase : BuilderBase, ISortBuilder> -{ - private static readonly SortDefinitionBuilder SortBuilder = Builders.Sort; - - public SortDefinition TransactionTimeStamp(bool ascending) - { - return Sort(ascending, nameof(DocumentBase.TransactionTimeStamp)); - } - - public SortDefinition TransactionId(bool ascending) - { - return Sort(ascending, nameof(DocumentBase.TransactionId)); - } - - public SortDefinition Combine(params SortDefinition[] sorts) - { - return SortBuilder.Combine(sorts); - } - - protected static SortDefinition Sort(bool ascending, string fieldName) - { - return ascending - ? SortBuilder.Ascending(fieldName) - : SortBuilder.Descending(fieldName); - } - - protected static SortDefinition SortDataType(bool ascending) - { - return Sort(ascending, DataTypeNameFieldName); - } - - protected virtual string[] GetHoistedFieldNames() - { - return Array.Empty(); - } -} 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/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 61c58ecf..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.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.ProvisionCollections(mongoSession.MongoDatabase.DatabaseNamespace + await mongoSession.MongoDatabase.Client.ProvisionSourceCollections(mongoSession.MongoDatabase + .DatabaseNamespace .DatabaseName, cancellationToken); _provisioned = true; @@ -64,10 +64,10 @@ await mongoSession.MongoDatabase.Client.ProvisionCollections(mongoSession.MongoD 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..0e5c4889 --- /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.Create( + 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..19db675c --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepository.cs @@ -0,0 +1,241 @@ +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; +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; + +namespace EntityDb.MongoDb.Sources; + +internal sealed 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(ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(sourceDataQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateSourceIds(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateSourceIds(ILeaseDataQuery leaseDataQuery, + CancellationToken cancellationToken = default) + { + return LeaseDataDocument + .GetQuery(leaseDataQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateSourceIds(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default) + { + return TagDataDocument + .GetQuery(tagDataQuery) + .EnumerateSourceIds(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateStatePointers(ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(sourceDataQuery) + .EnumerateSourceDataStatePointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateStatePointers(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateMessageStatePointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateStatePointers(ILeaseDataQuery leaseDataQuery, + CancellationToken cancellationToken = default) + { + return LeaseDataDocument + .GetQuery(leaseDataQuery) + .EnumerateMessageStatePointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateStatePointers(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default) + { + return TagDataDocument + .GetQuery(tagDataQuery) + .EnumerateMessageStatePointers(_mongoSession, cancellationToken); + } + + public IAsyncEnumerable EnumerateAgentSignatures(ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(sourceDataQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable EnumerateDeltas(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable EnumerateLeases(ILeaseDataQuery leaseDataQuery, + CancellationToken cancellationToken = default) + { + return LeaseDataDocument + .GetQuery(leaseDataQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable EnumerateTags(ITagDataQuery tagDataQuery, + CancellationToken cancellationToken = default) + { + return TagDataDocument + .GetQuery(tagDataQuery) + .EnumerateData(_mongoSession, _envelopeService, cancellationToken); + } + + public IAsyncEnumerable> EnumerateAnnotatedAgentSignatures( + ISourceDataQuery sourceDataQuery, + CancellationToken cancellationToken = default) + { + return AgentSignatureDocument + .GetQuery(sourceDataQuery) + .EnumerateEntitiesAnnotation(_mongoSession, _envelopeService, + cancellationToken); + } + + public IAsyncEnumerable> EnumerateAnnotatedDeltas(IMessageDataQuery messageDataQuery, + CancellationToken cancellationToken = default) + { + return DeltaDataDocument + .GetQuery(messageDataQuery) + .EnumerateAnnotatedSourceData(_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 DeltaDataDocument + .GetLastStateVersion(_mongoSession, message.StatePointer.Id, cancellationToken); + + if (message.StatePointer.StateVersion == StateVersion.Zero) + { + currentMessage = currentMessage with + { + StatePointer = currentMessage.StatePointer.Id + previousVersion.Next(), + }; + } + else + { + OptimisticConcurrencyException.ThrowIfMismatch(previousVersion.Next(), + message.StatePointer.StateVersion); + } + + 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 DeltaDataDocument + .GetInsertCommand(_envelopeService, source, message) + .Execute(_mongoSession, cancellationToken); + + if (message.AddLeases.Length > 0) + { + await LeaseDataDocument + .GetInsertCommand(_envelopeService, source, message) + .Execute(_mongoSession, cancellationToken); + } + + if (message.AddTags.Length > 0) + { + await TagDataDocument + .GetInsertCommand(_envelopeService, source, message) + .Execute(_mongoSession, cancellationToken); + } + + if (message.DeleteLeases.Length > 0) + { + await LeaseDataDocument + .GetDeleteCommand(message) + .Execute(_mongoSession, cancellationToken); + } + + if (message.DeleteTags.Length > 0) + { + await TagDataDocument + .GetDeleteCommand(message) + .Execute(_mongoSession, cancellationToken); + } + } +} diff --git a/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs new file mode 100644 index 00000000..68b87bc0 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/MongoDbSourceRepositoryFactory.cs @@ -0,0 +1,72 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Common.Sources; +using EntityDb.MongoDb.Sources.Sessions; +using Microsoft.Extensions.Options; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources; + +internal sealed class MongoDbSourceRepositoryFactory : DisposableResourceBaseClass, IMongoDbSourceRepositoryFactory +{ + private readonly IEnvelopeService _envelopeService; + private readonly IOptionsFactory _optionsFactory; + private readonly IServiceProvider _serviceProvider; + + public MongoDbSourceRepositoryFactory + ( + IServiceProvider serviceProvider, + IOptionsFactory optionsFactory, + IEnvelopeService envelopeService + ) + { + _serviceProvider = serviceProvider; + _optionsFactory = optionsFactory; + _envelopeService = envelopeService; + } + + public MongoDbSourceSessionOptions GetSourceSessionOptions(string sourceSessionOptionsName) + { + return _optionsFactory.Create(sourceSessionOptionsName); + } + + public async Task CreateSession(MongoDbSourceSessionOptions 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 ISourceRepository CreateRepository + ( + IMongoSession mongoSession + ) + { + ISourceRepository sourceRepository = new MongoDbSourceRepository + ( + mongoSession, + _envelopeService + ); + + sourceRepository = TryCatchSourceRepository.Create(_serviceProvider, sourceRepository); + + sourceRepository = PublishSourceRepository.Create(_serviceProvider, sourceRepository); + + return sourceRepository; + } +} 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/DataFilterBuilderBase.cs similarity index 58% rename from src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs rename to src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs index aa694f53..730d3847 100644 --- a/src/EntityDb.MongoDb/Queries/FilterBuilders/FilterBuilderBase.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/DataFilterBuilderBase.cs @@ -1,29 +1,37 @@ -using EntityDb.Abstractions.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; using MongoDB.Driver; -namespace EntityDb.MongoDb.Queries.FilterBuilders; +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; -internal abstract class FilterBuilderBase : BuilderBase, IFilterBuilder> +internal abstract class DataFilterBuilderBase : IDataFilterBuilder> { private static readonly FilterDefinitionBuilder FilterBuilder = Builders.Filter; - public FilterDefinition TransactionTimeStampGte(TimeStamp timeStamp) + public FilterDefinition SourceTimeStampGte(TimeStamp timeStamp) { - return Gte(nameof(DocumentBase.TransactionTimeStamp), timeStamp); + return Gte(nameof(DocumentBase.SourceTimeStamp), timeStamp); } - public FilterDefinition TransactionTimeStampLte(TimeStamp timeStamp) + public FilterDefinition SourceTimeStampLte(TimeStamp timeStamp) { - return Lte(nameof(DocumentBase.TransactionTimeStamp), timeStamp); + return Lte(nameof(DocumentBase.SourceTimeStamp), timeStamp); } - public FilterDefinition TransactionIdIn(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 +59,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 +74,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/LeaseDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseDataFilterBuilder.cs new file mode 100644 index 00000000..6cc25746 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/LeaseDataFilterBuilder.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 LeaseDataFilterBuilder : MessageDataFilterBuilder, + ILeaseDataFilterBuilder> +{ + public FilterDefinition LeaseScopeEq(string scope) + { + return Eq(nameof(LeaseDataDocument.Scope), scope); + } + + public FilterDefinition LeaseLabelEq(string label) + { + return Eq(nameof(LeaseDataDocument.Label), label); + } + + public FilterDefinition LeaseValueEq(string 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..8dfabeb6 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/MessageDataFilterBuilder.cs @@ -0,0 +1,32 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.States; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.FilterBuilders; + +internal class MessageDataFilterBuilder : DataFilterBuilderBase, + IMessageDataFilterBuilder> +{ + public FilterDefinition StateIdIn(params Id[] stateIds) + { + return Or + ( + stateIds + .Select(stateId => Eq(nameof(MessageDataDocumentBase.StateId), stateId)) + .ToArray() + ); + } + + public FilterDefinition StateVersionGte(StateVersion stateVersion) + { + return Gte(nameof(MessageDataDocumentBase.StateVersion), 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 new file mode 100644 index 00000000..c1ea8698 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/SourceDataFilterBuilder.cs @@ -0,0 +1,20 @@ +using EntityDb.Abstractions; +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 SourceDataFilterBuilder : DataFilterBuilderBase, + ISourceDataFilterBuilder> +{ + public FilterDefinition AnyStateIdIn(params Id[] stateIds) + { + return AnyIn + ( + nameof(SourceDataDocumentBase.StateIds), + stateIds + ); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagDataFilterBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagDataFilterBuilder.cs new file mode 100644 index 00000000..c8563811 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/FilterBuilders/TagDataFilterBuilder.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 TagDataFilterBuilder : MessageDataFilterBuilder, + ITagDataFilterBuilder> +{ + public FilterDefinition TagLabelEq(string label) + { + return Eq(nameof(TagDataDocument.Label), label); + } + + public FilterDefinition TagValueEq(string value) + { + return Eq(nameof(TagDataDocument.Value), value); + } +} diff --git a/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs b/src/EntityDb.MongoDb/Sources/Queries/MongoDbQueryOptions.cs similarity index 74% rename from src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs rename to src/EntityDb.MongoDb/Sources/Queries/MongoDbQueryOptions.cs index d7ad85dd..f8d5e3b3 100644 --- a/src/EntityDb.MongoDb/Queries/MongoDbQueryOptions.cs +++ b/src/EntityDb.MongoDb/Sources/Queries/MongoDbQueryOptions.cs @@ -1,11 +1,11 @@ using MongoDB.Driver; -namespace EntityDb.MongoDb.Queries; +namespace EntityDb.MongoDb.Sources.Queries; /// /// Options for configuring queries in MongoDb. /// -public class MongoDbQueryOptions +public sealed class MongoDbQueryOptions { /// /// Options for finding documents. diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/DataSortBuilderBase.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/DataSortBuilderBase.cs new file mode 100644 index 00000000..ba161907 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/DataSortBuilderBase.cs @@ -0,0 +1,38 @@ +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.MongoDb.Documents; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.Sources.Queries.SortBuilders; + +internal abstract class DataSortBuilderBase : IDataSortBuilder> +{ + private static readonly SortDefinitionBuilder SortBuilder = Builders.Sort; + + public SortDefinition SourceTimeStamp(bool ascending) + { + return Sort(ascending, nameof(DocumentBase.SourceTimeStamp)); + } + + public SortDefinition SourceId(bool ascending) + { + return Sort(ascending, nameof(DocumentBase.SourceId)); + } + + public SortDefinition DataType(bool ascending) + { + return Sort(ascending, nameof(DocumentBase.DataType)); + } + + public SortDefinition Combine(params SortDefinition[] sorts) + { + return SortBuilder.Combine(sorts); + } + + protected static SortDefinition Sort(bool ascending, string fieldName) + { + return ascending + ? SortBuilder.Ascending(fieldName) + : SortBuilder.Descending(fieldName); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.cs new file mode 100644 index 00000000..5c14001b --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/LeaseDataSortBuilder.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 LeaseDataSortBuilder : MessageDataSortBuilder, ILeaseDataSortBuilder> +{ + public SortDefinition LeaseScope(bool ascending) + { + return Sort(ascending, nameof(LeaseDataDocument.Scope)); + } + + public SortDefinition LeaseLabel(bool ascending) + { + return Sort(ascending, nameof(LeaseDataDocument.Label)); + } + + public SortDefinition LeaseValue(bool ascending) + { + return Sort(ascending, nameof(LeaseDataDocument.Value)); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageDataSortBuilder.cs new file mode 100644 index 00000000..e1729690 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/MessageDataSortBuilder.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 MessageDataSortBuilder : DataSortBuilderBase, + IMessageDataSortBuilder> +{ + public SortDefinition StateId(bool ascending) + { + return Sort(ascending, nameof(LeaseDataDocument.StateId)); + } + + public SortDefinition StateVersion(bool ascending) + { + 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..930b6d8e --- /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 : DataSortBuilderBase, + ISourceDataSortBuilder> +{ + public SortDefinition StateIds(bool ascending) + { + return Sort(ascending, nameof(AgentSignatureDocument.StateIds)); + } +} diff --git a/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.cs new file mode 100644 index 00000000..03081bc0 --- /dev/null +++ b/src/EntityDb.MongoDb/Sources/Queries/SortBuilders/TagDataSortBuilder.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 TagDataSortBuilder : MessageDataSortBuilder, ITagDataSortBuilder> +{ + public SortDefinition TagLabel(bool ascending) + { + return Sort(ascending, nameof(TagDataDocument.Label)); + } + + public SortDefinition TagValue(bool ascending) + { + return Sort(ascending, nameof(TagDataDocument.Value)); + } +} diff --git a/src/EntityDb.MongoDb/Sessions/IMongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/IMongoSession.cs similarity index 83% rename from src/EntityDb.MongoDb/Sessions/IMongoSession.cs rename to src/EntityDb.MongoDb/Sources/Sessions/IMongoSession.cs index aed64aa4..80bee7f2 100644 --- a/src/EntityDb.MongoDb/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.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/Sessions/MongoDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs similarity index 78% rename from src/EntityDb.MongoDb/Sessions/MongoDbTransactionSessionOptions.cs rename to src/EntityDb.MongoDb/Sources/Sessions/MongoDbSourceSessionOptions.cs index aa2daaae..d3c79d72 100644 --- a/src/EntityDb.MongoDb/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.Sessions; +namespace EntityDb.MongoDb.Sources.Sessions; /// -/// Configuration options for the MongoDb implementation of . +/// Configuration options for the MongoDb implementation of /// -public 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, Leases, Tags) /// public string DatabaseName { get; set; } = default!; @@ -34,10 +34,10 @@ 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)}"; + return $"{nameof(MongoDbSourceSessionOptions)}"; } } diff --git a/src/EntityDb.MongoDb/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs similarity index 91% rename from src/EntityDb.MongoDb/Sessions/MongoSession.cs rename to src/EntityDb.MongoDb/Sources/Sessions/MongoSession.cs index 257a8cd9..9733b3b6 100644 --- a/src/EntityDb.MongoDb/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.Sessions; +namespace EntityDb.MongoDb.Sources.Sessions; -internal record MongoSession +internal sealed 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 @@ -259,7 +258,7 @@ private void AssertNotReadOnly() { if (Options.ReadOnly) { - throw new CannotWriteInReadOnlyModeException(); + throw new ReadOnlyWriteException(); } } @@ -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/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs similarity index 79% rename from src/EntityDb.MongoDb/Sessions/TestModeMongoSession.cs rename to src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs index 62269fe5..247fab83 100644 --- a/src/EntityDb.MongoDb/Sessions/TestModeMongoSession.cs +++ b/src/EntityDb.MongoDb/Sources/Sessions/TestModeMongoSession.cs @@ -1,11 +1,11 @@ using EntityDb.Common.Disposables; -using EntityDb.MongoDb.Queries; +using EntityDb.MongoDb.Sources.Queries; using MongoDB.Bson; using MongoDB.Driver; -namespace EntityDb.MongoDb.Sessions; +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; @@ -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 53% rename from src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs rename to src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs index feaf17a9..74e0b86e 100644 --- a/src/EntityDb.MongoDb/Transactions/TestModeMongoDbTransactionRepositoryFactory.cs +++ b/src/EntityDb.MongoDb/Sources/TestModeMongoDbSourceRepositoryFactory.cs @@ -1,31 +1,29 @@ -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Sources.Sessions; -namespace EntityDb.MongoDb.Transactions; +namespace EntityDb.MongoDb.Sources; -internal class - TestModeMongoDbTransactionRepositoryFactory : MongoDbTransactionRepositoryFactoryWrapper +internal sealed class 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 +35,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/States/AutoProvisionMongoDbStateRepositoryFactory.cs b/src/EntityDb.MongoDb/States/AutoProvisionMongoDbStateRepositoryFactory.cs new file mode 100644 index 00000000..5274bb87 --- /dev/null +++ b/src/EntityDb.MongoDb/States/AutoProvisionMongoDbStateRepositoryFactory.cs @@ -0,0 +1,84 @@ +using EntityDb.MongoDb.Extensions; +using EntityDb.MongoDb.States.Sessions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EntityDb.MongoDb.States; + +internal sealed class + AutoProvisionMongoDbStateRepositoryFactory : MongoDbStateRepositoryFactoryWrapper +{ + // ReSharper disable once StaticMemberInGenericType + private static readonly SemaphoreSlim Lock = new(1); + + // ReSharper disable once StaticMemberInGenericType + private static bool _provisioned; + + private readonly ILogger> _logger; + + public AutoProvisionMongoDbStateRepositoryFactory + ( + ILogger> logger, + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory + ) + : base(mongoDbStateRepositoryFactory) + { + _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(MongoDbStateSessionOptions options, + CancellationToken cancellationToken) + { + var mongoSession = await base.CreateSession(options, cancellationToken); + + await AcquireLock(cancellationToken); + + if (_provisioned) + { + ReleaseLock(); + + return mongoSession; + } + + await mongoSession.MongoDatabase.Client.ProvisionStateCollection + ( + mongoSession.MongoDatabase.DatabaseNamespace.DatabaseName, + mongoSession.CollectionName, + cancellationToken + ); + + _provisioned = true; + + _logger.LogInformation("MongoDb has been auto-provisioned"); + + ReleaseLock(); + + return mongoSession; + } + + public static IMongoDbStateRepositoryFactory Create(IServiceProvider serviceProvider, + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory) + { + return ActivatorUtilities.CreateInstance>( + serviceProvider, + 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..e6715f12 --- /dev/null +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepository.cs @@ -0,0 +1,81 @@ +using EntityDb.Abstractions.States; +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 sealed 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(StatePointer 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.StateVersion, + StatePointer = statePointer, + }, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + await _mongoSession.CommitTransaction(cancellationToken); + + return true; + } + catch + { + await _mongoSession.AbortTransaction(); + + throw; + } + } + + public async Task Get(StatePointer 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(StatePointer[] 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/States/MongoDbStateRepositoryFactory.cs b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs new file mode 100644 index 00000000..b0230e92 --- /dev/null +++ b/src/EntityDb.MongoDb/States/MongoDbStateRepositoryFactory.cs @@ -0,0 +1,70 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Common.States; +using EntityDb.MongoDb.States.Sessions; +using Microsoft.Extensions.Options; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.States; + +internal sealed class MongoDbStateRepositoryFactory : DisposableResourceBaseClass, + IMongoDbStateRepositoryFactory + where TState : notnull +{ + private readonly IEnvelopeService _envelopeService; + private readonly IOptionsFactory _optionsFactory; + private readonly IServiceProvider _serviceProvider; + + public MongoDbStateRepositoryFactory + ( + IServiceProvider serviceProvider, + IOptionsFactory optionsFactory, + IEnvelopeService envelopeService + ) + { + _serviceProvider = serviceProvider; + _optionsFactory = optionsFactory; + _envelopeService = envelopeService; + } + + public MongoDbStateSessionOptions GetSessionOptions(string stateSessionOptionsName) + { + return _optionsFactory.Create(stateSessionOptionsName); + } + + public async Task CreateSession(MongoDbStateSessionOptions 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 IStateRepository CreateRepository + ( + IMongoSession mongoSession + ) + { + var mongoDbStateRepository = new MongoDbStateRepository + ( + _envelopeService, + mongoSession + ); + + 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..748ad9c8 --- /dev/null +++ b/src/EntityDb.MongoDb/States/Sessions/IMongoSession.cs @@ -0,0 +1,24 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.States; +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(StatePointer statePointer, CancellationToken cancellationToken); + + Task Delete(StatePointer[] statePointer, CancellationToken cancellationToken); + + void StartTransaction(); + Task CommitTransaction(CancellationToken cancellationToken); + Task AbortTransaction(); + + IMongoSession WithSessionOptions(MongoDbStateSessionOptions options); +} diff --git a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs b/src/EntityDb.MongoDb/States/Sessions/MongoDbStateSessionOptions.cs similarity index 57% rename from src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs rename to src/EntityDb.MongoDb/States/Sessions/MongoDbStateSessionOptions.cs index 41ae493c..9d83d369 100644 --- a/src/EntityDb.SqlDb/Sessions/SqlDbTransactionSessionOptions.cs +++ b/src/EntityDb.MongoDb/States/Sessions/MongoDbStateSessionOptions.cs @@ -1,48 +1,49 @@ -using EntityDb.Abstractions.Transactions; -using System.Data; +using EntityDb.Abstractions.States; +using EntityDb.MongoDb.Sources.Sessions; +using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.SqlDb.Sessions; +namespace EntityDb.MongoDb.States.Sessions; /// -/// Configuration options for the PostgreSql implementation of . +/// Configuration options for the MongoDb implementation of . /// -public class SqlDbTransactionSessionOptions +public sealed class MongoDbStateSessionOptions { /// - /// A connection string that is compatible with + /// A connection string that is compatible with /// public string ConnectionString { get; set; } = default!; /// - /// If true, indicates the agent only intends to execute queries. + /// The name of the database that contains the states /// - public bool ReadOnly { get; set; } + public string DatabaseName { get; set; } = default!; /// - /// If true, indicates the agent can tolerate replication lag for queries. + /// The name of the collection that contains the states /// - public bool SecondaryPreferred { get; set; } + public string CollectionName { get; set; } = default!; /// - /// Determines the isolation level for transactions. + /// If true, indicates the agent only intends to execute queries. /// - public IsolationLevel IsolationLevel { get; set; } = IsolationLevel.Snapshot; + public bool ReadOnly { get; set; } /// - /// Determines how long to wait before a command should be automatically aborted. + /// If true, indicates the agent can tolerate replication lag for queries. /// - public TimeSpan WriteTimeout { get; set; } + public bool SecondaryPreferred { get; set; } /// - /// Determines how long to wait before a query should be automatically killed. + /// Determines how long to wait before a command should be automatically aborted. /// - public TimeSpan? ReadTimeout { get; set; } + public TimeSpan? WriteTimeout { get; set; } - /// + /// [ExcludeFromCodeCoverage(Justification = "This is only overridden to make test names better.")] public override string ToString() { - return $"{nameof(SqlDbTransactionSessionOptions)}"; + return $"{nameof(MongoDbSourceSessionOptions)}"; } } diff --git a/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs new file mode 100644 index 00000000..eba20179 --- /dev/null +++ b/src/EntityDb.MongoDb/States/Sessions/MongoSession.cs @@ -0,0 +1,256 @@ +using EntityDb.Abstractions.States; +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.States.Sessions; + +internal sealed record MongoSession +( + ILogger Logger, + IMongoDatabase MongoDatabase, + IClientSessionHandle ClientSessionHandle, + MongoDbStateSessionOptions Options +) : DisposableResourceBaseRecord, IMongoSession +{ + 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; + + public async Task Upsert(StateDocument stateDocument, 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(stateDocument.StatePointer), + stateDocument, + UpsertOptions, + cancellationToken + ); + + Logger + .LogInformation + ( + "Finished Running MongoDb Upsert on `{DatabaseNamespace}.{CollectionName}`\n\nServer Session Id: {ServerSessionId}", + MongoDatabase.DatabaseNamespace, + Options.CollectionName, + serverSessionId + ); + } + + public async Task Fetch + ( + StatePointer statePointer, + CancellationToken cancellationToken + ) + { + var filter = GetFilter(statePointer); + + 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 stateDocument = 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, + stateDocument != null + ); + + return stateDocument; + } + + public async Task Delete(StatePointer[] statePointer, CancellationToken cancellationToken) + { + AssertNotReadOnly(); + + var filter = Filter.And(statePointer.Select(GetFilter)); + + 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(MongoDbStateSessionOptions 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 static FilterDefinition GetFilter(StatePointer statePointer) + { + return Filter.Eq(document => document.StatePointer, statePointer); + } + + private ReadPreference GetReadPreference() + { + if (!Options.ReadOnly) + { + return ReadPreference.Primary; + } + + return Options.SecondaryPreferred + ? ReadPreference.SecondaryPreferred + : ReadPreference.PrimaryPreferred; + } + + [ExcludeFromCodeCoverage(Justification = "Tests should always run in a source.")] + private ReadConcern GetReadConcern() + { + return ClientSessionHandle.IsInTransaction + ? ReadConcern.Snapshot + : ReadConcern.Majority; + } + + private void AssertNotReadOnly() + { + if (Options.ReadOnly) + { + throw new ReadOnlyWriteException(); + } + } + + public static IMongoSession Create + ( + IServiceProvider serviceProvider, + IMongoDatabase mongoDatabase, + IClientSessionHandle clientSessionHandle, + MongoDbStateSessionOptions options + ) + { + return ActivatorUtilities.CreateInstance(serviceProvider, mongoDatabase, clientSessionHandle, + options); + } +} diff --git a/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs new file mode 100644 index 00000000..42daf6fc --- /dev/null +++ b/src/EntityDb.MongoDb/States/Sessions/TestModeMongoSession.cs @@ -0,0 +1,54 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; +using EntityDb.MongoDb.Documents; +using MongoDB.Driver; + +namespace EntityDb.MongoDb.States.Sessions; + +internal sealed 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(StatePointer statePointer, CancellationToken cancellationToken) + { + return MongoSession.Fetch + ( + statePointer, + cancellationToken + ); + } + + public Task Delete(StatePointer[] 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/States/TestModeMongoDbStateRepositoryFactory.cs b/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs new file mode 100644 index 00000000..44638988 --- /dev/null +++ b/src/EntityDb.MongoDb/States/TestModeMongoDbStateRepositoryFactory.cs @@ -0,0 +1,51 @@ +using EntityDb.MongoDb.States.Sessions; + +namespace EntityDb.MongoDb.States; + +internal sealed class TestModeMongoDbStateRepositoryFactory : MongoDbStateRepositoryFactoryWrapper +{ + private (IMongoSession Normal, TestModeMongoSession TestMode)? _sessions; + + public TestModeMongoDbStateRepositoryFactory + ( + IMongoDbStateRepositoryFactory mongoDbStateRepositoryFactory + ) + : base(mongoDbStateRepositoryFactory) + { + } + + public override async Task CreateSession(MongoDbStateSessionOptions options, + CancellationToken cancellationToken) + { + if (_sessions.HasValue) + { + return _sessions.Value.TestMode + .WithSessionOptions(options); + } + + var normalOptions = new MongoDbStateSessionOptions + { + 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/IMongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs deleted file mode 100644 index a1664268..00000000 --- a/src/EntityDb.MongoDb/Transactions/IMongoDbTransactionRepositoryFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Transactions; -using EntityDb.MongoDb.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 7f84b54b..00000000 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepository.cs +++ /dev/null @@ -1,246 +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.Transactions.Steps; -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.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 transactionStep in transaction.Steps) - { - 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() - }); - } - - 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, IAppendCommandTransactionStep appendCommandTransactionStep, - 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) - .Execute(_mongoSession, cancellationToken); - } - - private async Task PutLeases(ITransaction transaction, IAddLeasesTransactionStep addLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetInsertCommand(_envelopeService, transaction, addLeasesTransactionStep) - .Execute(_mongoSession, cancellationToken); - } - - private async Task PutTags(ITransaction transaction, IAddTagsTransactionStep addTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetInsertCommand(_envelopeService, transaction, addTagsTransactionStep) - .Execute(_mongoSession, cancellationToken); - } - - private async Task DeleteLeases(IDeleteLeasesTransactionStep deleteLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetDeleteCommand(deleteLeasesTransactionStep) - .Execute(_mongoSession, cancellationToken); - } - - private async Task DeleteTags(IDeleteTagsTransactionStep deleteTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetDeleteCommand(deleteTagsTransactionStep) - .Execute(_mongoSession, cancellationToken); - } -} diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs deleted file mode 100644 index 99579cbe..00000000 --- a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactory.cs +++ /dev/null @@ -1,79 +0,0 @@ -using EntityDb.Abstractions.Transactions; -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.Driver; - -namespace EntityDb.MongoDb.Transactions; - -internal class MongoDbTransactionRepositoryFactory : DisposableResourceBaseClass, IMongoDbTransactionRepositoryFactory -{ - private readonly IEnvelopeService _envelopeService; - 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, - IOptionsFactory optionsFactory, - IEnvelopeService envelopeService - ) - { - _serviceProvider = serviceProvider; - _optionsFactory = optionsFactory; - _envelopeService = envelopeService; - } - - public MongoDbTransactionSessionOptions GetTransactionSessionOptions(string transactionSessionOptionsName) - { - return _optionsFactory.Create(transactionSessionOptionsName); - } - - public async Task CreateSession(MongoDbTransactionSessionOptions 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 ITransactionRepository CreateRepository - ( - IMongoSession mongoSession - ) - { - var mongoDbTransactionRepository = new MongoDbTransactionRepository - ( - mongoSession, - _envelopeService - ); - - return TryCatchTransactionRepository.Create(_serviceProvider, mongoDbTransactionRepository); - } -} diff --git a/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs b/src/EntityDb.MongoDb/Transactions/MongoDbTransactionRepositoryFactoryWrapper.cs deleted file mode 100644 index d09474c8..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.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.MongoDb/packages.lock.json b/src/EntityDb.MongoDb/packages.lock.json index 8e4e3579..1c9b66b0 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.23.1, )", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "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.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" } }, "System.Linq.Async": { @@ -23,6 +23,174 @@ "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", + "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", @@ -57,31 +225,33 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "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.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "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.18.0", - "MongoDB.Libmongocrypt": "1.6.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": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "SharpCompress": { "type": "Transitive", @@ -98,6 +268,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", @@ -119,8 +294,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", 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..c9ec0987 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 sealed 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/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.Mvc/EntityDb.Mvc.csproj b/src/EntityDb.Mvc/EntityDb.Mvc.csproj index fe0bc4f0..6c61df58 100644 --- a/src/EntityDb.Mvc/EntityDb.Mvc.csproj +++ b/src/EntityDb.Mvc/EntityDb.Mvc.csproj @@ -1,9 +1,8 @@  - - 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. + 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/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.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 b09491b1..00000000 --- a/src/EntityDb.Npgsql/Converters/NpgsqlConverter.cs +++ /dev/null @@ -1,284 +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; -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 GetInFitler(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(filterDefinition => GetFilter(filterDefinition, parameters)) - ), - - OrFilterDefinition orFilterDefinition => string.Join - ( - " OR ", - orFilterDefinition.FilterDefinitions.Select(filterDefinition => GetFilter(filterDefinition, parameters)) - ), - - NotFilterDefinition notFilterDefinition => $"NOT {GetFilter(notFilterDefinition.FilterDefinition, parameters)}", - - EqFilterDefinition eqFilterDefinition => GetEqFilter(eqFilterDefinition, parameters), - - InFilterDefinition inFilterDefinition => GetInFitler(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(sortDefinition => GetSort(tableName, options, sortDefinition)) - ), - - 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 26572694..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 dfa0cded..00000000 --- a/src/EntityDb.Npgsql/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,56 +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.Configuration; -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 46f80ebe..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 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 1116abe9..00000000 --- a/src/EntityDb.Npgsql/packages.lock.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net7.0": { - "Npgsql": { - "type": "Direct", - "requested": "[7.0.0, )", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "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==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "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 23a1e576..16f2f8f6 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateCollections.cs @@ -5,18 +5,8 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class CreateCollections : CommandBase +internal sealed 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.ProvisionCollections(arguments.ServiceName); + await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } - protected static void AddClusterNameArgument(Command command) + private 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); @@ -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 sealed 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..221b0d68 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateRole.cs @@ -5,16 +5,8 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class CreateRole : CommandBase +internal sealed 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 = 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[] { - 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 sealed 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..1265a225 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Cluster/CreateUser.cs @@ -4,17 +4,8 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Cluster; -internal class CreateUser : CommandBase +internal sealed 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 sealed record Arguments + ( + string GroupName, + string PublicKey, + string PrivateKey, + string ServiceName, + string ServicePassword + ); } 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 6a6b7ba0..2e44704c 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/CreateCollectionsServerless.cs @@ -5,18 +5,8 @@ namespace EntityDb.Provisioner.Commands.MongoDb.Atlas.Serverless; -internal class CreateCollectionsServerless : CommandBase +internal sealed 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.ProvisionCollections(arguments.ServiceName); + await mongoClient.ProvisionSourceCollections(arguments.ServiceName); } - protected static void AddServerlessNameArgument(Command command) + private 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 sealed 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 85% rename from src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbServerlessCommand.cs rename to src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbAtlasServerlessCommand.cs index 847b71fb..97564f73 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/Atlas/Serverless/MongoDbServerlessCommand.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/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 3cdc87a3..1c223dab 100644 --- a/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs +++ b/src/EntityDb.Provisioner/Commands/MongoDb/CreateCollections.cs @@ -5,26 +5,20 @@ namespace EntityDb.Provisioner.Commands.MongoDb; -internal class CreateCollections : CommandBase +internal sealed 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.ProvisionCollections(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 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/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..8dfdea37 100644 --- a/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj +++ b/src/EntityDb.Provisioner/EntityDb.Provisioner.csproj @@ -1,26 +1,23 @@  - - - Exe - + + Exe + - - - true - entitydb-provisioner - EntityDb EventSourcing DDD CQRS - A dotnet tool for provisioning databases. - + + true + entitydb-provisioner + EntityDb EventSourcing EventStreaming DDD CQRS + A dotnet tool for provisioning databases. + - - - - + + + + - - - - + + + diff --git a/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs b/src/EntityDb.Provisioner/MongoDbAtlas/DigestChallengeRequest.cs index 36acbde8..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 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/Resource.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs similarity index 87% rename from src/EntityDb.Provisioner/MongoDbAtlas/Models/Resource.cs rename to src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasResource.cs index fac64e36..5902c639 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/Resource.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/RoleAction.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs similarity index 85% rename from src/EntityDb.Provisioner/MongoDbAtlas/Models/RoleAction.cs rename to src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasRoleAction.cs index 6472d97d..c677748e 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/RoleAction.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/UserRole.cs b/src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs similarity index 85% rename from src/EntityDb.Provisioner/MongoDbAtlas/Models/UserRole.cs rename to src/EntityDb.Provisioner/MongoDbAtlas/Models/MongoDbAtlasUserRole.cs index 731db300..6c51666a 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/UserRole.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 20cd7196..5e8b6620 100644 --- a/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs +++ b/src/EntityDb.Provisioner/MongoDbAtlas/Models/ServerlessInstance.cs @@ -2,7 +2,8 @@ namespace EntityDb.Provisioner.MongoDbAtlas.Models; -internal class ServerlessInstance +internal sealed 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..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(); @@ -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.Provisioner/packages.lock.json b/src/EntityDb.Provisioner/packages.lock.json index be1e68a4..564161be 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", @@ -41,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", @@ -60,51 +73,44 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "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.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "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.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "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.18.0", - "MongoDB.Libmongocrypt": "1.6.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": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" - }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "SharpCompress": { "type": "Transitive", @@ -121,10 +127,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", @@ -142,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", @@ -158,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.18.0, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { + "entitydb.common": { "type": "Project", "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", + "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/ConnectionMultiplexers/ConnectionMultiplexerPool.cs b/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs similarity index 90% rename from src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerPool.cs rename to src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerFactory.cs index 87ad8189..7913526c 100644 --- a/src/EntityDb.Redis/ConnectionMultiplexers/ConnectionMultiplexerPool.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/EntityDb.Redis.csproj b/src/EntityDb.Redis/EntityDb.Redis.csproj index 83f4a193..a0d5b583 100644 --- a/src/EntityDb.Redis/EntityDb.Redis.csproj +++ b/src/EntityDb.Redis/EntityDb.Redis.csproj @@ -1,18 +1,17 @@  - - 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. - + - - + + \ No newline at end of file diff --git a/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs b/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs index c184b7e1..19b82932 100644 --- a/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs +++ b/src/EntityDb.Redis/Extensions/ServiceCollectionExtensions.cs @@ -1,10 +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 Microsoft.Extensions.Configuration; +using EntityDb.Redis.States; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics.CodeAnalysis; @@ -22,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 ); @@ -43,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/Sessions/IRedisSession.cs b/src/EntityDb.Redis/Sessions/IRedisSession.cs deleted file mode 100644 index 9f936efd..00000000 --- a/src/EntityDb.Redis/Sessions/IRedisSession.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EntityDb.Abstractions.Disposables; -using EntityDb.Abstractions.ValueObjects; -using StackExchange.Redis; - -namespace EntityDb.Redis.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/Snapshots/RedisSnapshotRepository.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepository.cs deleted file mode 100644 index 4c43a6ec..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.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/RedisSnapshotRepositoryFactory.cs b/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs deleted file mode 100644 index 7426383f..00000000 --- a/src/EntityDb.Redis/Snapshots/RedisSnapshotRepositoryFactory.cs +++ /dev/null @@ -1,70 +0,0 @@ -using EntityDb.Abstractions.Snapshots; -using EntityDb.Common.Disposables; -using EntityDb.Common.Envelopes; -using EntityDb.Common.Snapshots; -using EntityDb.Redis.ConnectionMultiplexers; -using EntityDb.Redis.Sessions; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace EntityDb.Redis.Snapshots; - -internal class RedisSnapshotRepositoryFactory : DisposableResourceBaseClass, - ISnapshotRepositoryFactory -{ - private readonly ConnectionMultiplexerFactory _connectionMultiplexerFactory; - private readonly IEnvelopeService _envelopeService; - private readonly IOptionsFactory> _optionsFactory; - private readonly IServiceProvider _serviceProvider; - - public RedisSnapshotRepositoryFactory - ( - IServiceProvider serviceProvider, - ConnectionMultiplexerFactory connectionMultiplexerFactory, - IOptionsFactory> optionsFactory, - IEnvelopeService envelopeService - ) - { - _serviceProvider = serviceProvider; - _connectionMultiplexerFactory = connectionMultiplexerFactory; - _optionsFactory = optionsFactory; - _envelopeService = envelopeService; - } - - public async Task> CreateRepository(string snapshotSessionOptionsName, - CancellationToken cancellationToken = default) - { - var options = _optionsFactory.Create(snapshotSessionOptionsName); - - var redisSession = await CreateSession(options, cancellationToken); - - var redisSnapshotRepository = new RedisSnapshotRepository - ( - _envelopeService, - redisSession - ); - - return TryCatchSnapshotRepository.Create(_serviceProvider, redisSnapshotRepository); - } - - - private async Task CreateSession(RedisSnapshotSessionOptions options, - CancellationToken cancellationToken) - { - var connectionMultiplexer = - 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/States/RedisStateRepository.cs b/src/EntityDb.Redis/States/RedisStateRepository.cs new file mode 100644 index 00000000..a3b8cbb1 --- /dev/null +++ b/src/EntityDb.Redis/States/RedisStateRepository.cs @@ -0,0 +1,55 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Redis.States.Sessions; + +namespace EntityDb.Redis.States; + +internal sealed 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(StatePointer statePointer, TState state, + CancellationToken cancellationToken = default) + { + var stateValue = _envelopeService + .Serialize(state); + + return await _redisSession.Upsert(statePointer, stateValue).WaitAsync(cancellationToken); + } + + public async Task Get(StatePointer statePointer, + CancellationToken cancellationToken = default) + { + var stateValue = await _redisSession.Fetch(statePointer).WaitAsync(cancellationToken); + + if (!stateValue.HasValue) + { + return default; + } + + return _envelopeService + .Deserialize(stateValue!); + } + + public Task Delete(StatePointer[] statePointers, CancellationToken cancellationToken = default) + { + return _redisSession.Delete(statePointers).WaitAsync(cancellationToken); + } + + public override async ValueTask DisposeAsync() + { + await _redisSession.DisposeAsync(); + } +} diff --git a/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs b/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs new file mode 100644 index 00000000..7f184a23 --- /dev/null +++ b/src/EntityDb.Redis/States/RedisStateRepositoryFactory.cs @@ -0,0 +1,59 @@ +using EntityDb.Abstractions.States; +using EntityDb.Common.Disposables; +using EntityDb.Common.Envelopes; +using EntityDb.Common.States; +using EntityDb.Redis.ConnectionMultiplexers; +using EntityDb.Redis.States.Sessions; +using Microsoft.Extensions.Options; + +namespace EntityDb.Redis.States; + +internal sealed class RedisStateRepositoryFactory : DisposableResourceBaseClass, + IStateRepositoryFactory +{ + private readonly ConnectionMultiplexerFactory _connectionMultiplexerFactory; + private readonly IEnvelopeService _envelopeService; + private readonly IOptionsFactory _optionsFactory; + private readonly IServiceProvider _serviceProvider; + + public RedisStateRepositoryFactory + ( + IServiceProvider serviceProvider, + ConnectionMultiplexerFactory connectionMultiplexerFactory, + IOptionsFactory optionsFactory, + IEnvelopeService envelopeService + ) + { + _serviceProvider = serviceProvider; + _connectionMultiplexerFactory = connectionMultiplexerFactory; + _optionsFactory = optionsFactory; + _envelopeService = envelopeService; + } + + public async Task> Create(string stateSessionOptionsName, + CancellationToken cancellationToken = default) + { + var options = _optionsFactory.Create(stateSessionOptionsName); + + var redisSession = await CreateSession(options, cancellationToken); + + var redisStateRepository = new RedisStateRepository + ( + _envelopeService, + redisSession + ); + + return TryCatchStateRepository.Create(_serviceProvider, redisStateRepository); + } + + + private async Task CreateSession(RedisStateSessionOptions options, + CancellationToken cancellationToken) + { + var connectionMultiplexer = + await _connectionMultiplexerFactory.CreateConnectionMultiplexer(options.ConnectionString, + cancellationToken); + + return RedisSession.Create(_serviceProvider, connectionMultiplexer.GetDatabase(), options); + } +} diff --git a/src/EntityDb.Redis/States/Sessions/IRedisSession.cs b/src/EntityDb.Redis/States/Sessions/IRedisSession.cs new file mode 100644 index 00000000..a1b74175 --- /dev/null +++ b/src/EntityDb.Redis/States/Sessions/IRedisSession.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Disposables; +using EntityDb.Abstractions.States; +using StackExchange.Redis; + +namespace EntityDb.Redis.States.Sessions; + +internal interface IRedisSession : IDisposableResource +{ + Task Upsert(StatePointer statePointer, RedisValue redisValue); + Task Fetch(StatePointer statePointer); + Task Delete(StatePointer[] statePointers); +} diff --git a/src/EntityDb.Redis/Sessions/RedisSession.cs b/src/EntityDb.Redis/States/Sessions/RedisSession.cs similarity index 63% rename from src/EntityDb.Redis/Sessions/RedisSession.cs rename to src/EntityDb.Redis/States/Sessions/RedisSession.cs index 78a24705..13576539 100644 --- a/src/EntityDb.Redis/Sessions/RedisSession.cs +++ b/src/EntityDb.Redis/States/Sessions/RedisSession.cs @@ -1,24 +1,24 @@ -using EntityDb.Abstractions.ValueObjects; +using EntityDb.Abstractions.States; using EntityDb.Common.Disposables; using EntityDb.Common.Exceptions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StackExchange.Redis; -namespace EntityDb.Redis.Sessions; +namespace EntityDb.Redis.States.Sessions; -internal sealed record RedisSession +internal sealed record RedisSession ( - ILogger> Logger, + ILogger Logger, IDatabase Database, - RedisSnapshotSessionOptions Options + RedisStateSessionOptions Options ) : DisposableResourceBaseRecord, IRedisSession { - public async Task Insert(Pointer snapshotPointer, RedisValue redisValue) + public async Task Upsert(StatePointer 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\nInserted: {Inserted}", + "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(StatePointer 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(StatePointer[] 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,22 +126,22 @@ private void AssertNotReadOnly() { if (Options.ReadOnly) { - throw new CannotWriteInReadOnlyModeException(); + throw new ReadOnlyWriteException(); } } - private RedisKey GetSnapshotKey(Pointer snapshotPointer) + private RedisKey GetRedisKey(StatePointer statePointer) { - return $"{Options.KeyNamespace}#{snapshotPointer.Id.Value}@{snapshotPointer.VersionNumber.Value}"; + return $"{Options.KeyNamespace}#{statePointer}"; } public static IRedisSession Create ( IServiceProvider serviceProvider, IDatabase database, - RedisSnapshotSessionOptions options + RedisStateSessionOptions 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/States/Sessions/RedisStateSessionOptions.cs similarity index 64% rename from src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs rename to src/EntityDb.Redis/States/Sessions/RedisStateSessionOptions.cs index 65c27f7b..a3fe8717 100644 --- a/src/EntityDb.Redis/Sessions/RedisSnapshotSessionOptions.cs +++ b/src/EntityDb.Redis/States/Sessions/RedisStateSessionOptions.cs @@ -1,22 +1,22 @@ -using EntityDb.Abstractions.Snapshots; +using EntityDb.Abstractions.States; using StackExchange.Redis; using System.Diagnostics.CodeAnalysis; -namespace EntityDb.Redis.Sessions; +namespace EntityDb.Redis.States.Sessions; /// -/// Configuration options for the Redis implementation of . +/// Configuration options for the Redis implementation of . /// -public class RedisSnapshotSessionOptions +public sealed class RedisStateSessionOptions { /// - /// A connection string that is compatible with + /// A connection string that is compatible with /// public string ConnectionString { get; set; } = default!; /// - /// Choose a key namspace for snapshots. Snapshots are stored with keys in the following format: - /// {KeyNamespace}#{SnapshotId}@{SnapshotVersionNumber} + /// Choose a key namespace for states. States are stored with keys in the following format: + /// {KeyNamespace}#{StateId}@{StateVersion} /// public string KeyNamespace { get; set; } = default!; @@ -30,10 +30,10 @@ 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}"; + return $"{nameof(RedisStateSessionOptions)}"; } } diff --git a/src/EntityDb.Redis/packages.lock.json b/src/EntityDb.Redis/packages.lock.json index 7b3af677..26ed44de 100644 --- a/src/EntityDb.Redis/packages.lock.json +++ b/src/EntityDb.Redis/packages.lock.json @@ -4,12 +4,12 @@ "net7.0": { "StackExchange.Redis": { "type": "Direct", - "requested": "[2.6.70, )", - "resolved": "2.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "requested": "[2.7.10, )", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.Linq.Async": { @@ -26,104 +26,87 @@ "resolved": "6.0.0", "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "Microsoft.NETCore.Platforms": { + "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" }, - "Microsoft.Win32.Registry": { + "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.IO.Pipelines": "5.0.1" } }, - "Microsoft.Win32.SystemEvents": { + "System.IO.Pipelines": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "Bh6blKG8VAKvXiLe2L+sEsn62nc1Ij34MrNxepD2OCrS5cpCwQa9MeLyhVQPQ/R4Wlzwuy6wMK8hLb11QPDRsQ==", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "entitydb.abstractions": { + "type": "Project", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0" + "System.Linq.Async": "[6.0.1, )" } }, - "Pipelines.Sockets.Unofficial": { - "type": "Transitive", - "resolved": "2.2.2", - "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "entitydb.common": { + "type": "Project", "dependencies": { - "System.IO.Pipelines": "5.0.1" + "EntityDb.Abstractions": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "aM7cbfEfVNlEEOj3DsZP+2g9NRwbkyiAv2isQEzw7pnkDg9ekCU2m1cdJLM02Uq691OaCS91tooaxcEn8d0q5w==", + "entitydb.json": { + "type": "Project", "dependencies": { - "System.Security.Cryptography.ProtectedData": "5.0.0", - "System.Security.Permissions": "5.0.0" + "EntityDb.Common": "[1.0.0, )", + "System.Linq.Async": "[6.0.1, )" } - }, - "System.Diagnostics.PerformanceCounter": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", + } + }, + "net8.0": { + "StackExchange.Redis": { + "type": "Direct", + "requested": "[2.7.10, )", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "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" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SztFwAnpfKC8+sEKXAFxCBWhKQaEd97EiOL7oZJZP56zbqnLpmxACWA8aGseaUExciuEAUuR9dY8f7HkTRAdnw==", + "System.Linq.Async": { + "type": "Direct", + "requested": "[6.0.1, )", + "resolved": "6.0.1", + "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", "dependencies": { - "Microsoft.Win32.SystemEvents": "5.0.0" + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" - }, - "System.Security.AccessControl": { + "Microsoft.Bcl.AsyncInterfaces": { "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" - } + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "System.Security.Cryptography.ProtectedData": { + "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "HGxMSAFAPLNoxBvSfW08vHde0F9uh7BjASwu6JF9JnXuEPhCY3YUqURn0+bQV/4UWeaqymmrHWV+Aw9riQCtCA==" + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" }, - "System.Security.Permissions": { + "Pipelines.Sockets.Unofficial": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "uE8juAhEkp7KDBCdjDIE3H9R1HJuEHqeqX8nLX9gmYKWwsqk3T5qZlPx8qle5DPKimC/Fy3AFTdV7HamgCh9qQ==", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Windows.Extensions": "5.0.0" + "System.IO.Pipelines": "5.0.1" } }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Windows.Extensions": { + "System.IO.Pipelines": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "c1ho9WU9ZxMZawML+ssPKZfdnrg/OjR3pe0m9v8230z3acqphwvPJqzAkH54xRYm5ntZHGG1EPP3sux9H3qSPg==", - "dependencies": { - "System.Drawing.Common": "5.0.0" - } + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" }, "entitydb.abstractions": { "type": "Project", 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 a089688d..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 3fba7946..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) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs deleted file mode 100644 index c2656b49..00000000 --- a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocument.cs +++ /dev/null @@ -1,82 +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 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.Steps - .Select(transactionStep => transactionStep.EntityId) - .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 f9dd2a39..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)), - TransactionTimeStamp = new TimeStamp(await dbDataReader.GetFieldValueAsync(_transactionTimeStampOrdinal)), - EntityIds = (await dbDataReader.GetFieldValueAsync(_entityIdsOrdinal)) - .Select(guid => new Id(guid)) - .ToArray(), - DataType = await dbDataReader.GetFieldValueAsync(_dataTypeOrdinal), - Data = await dbDataReader.GetFieldValueAsync(_dataOrdinal), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/AgentSignature/AgentSignatureDocumentReaderBase.cs deleted file mode 100644 index 1e8b0040..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 b18758d5..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)) - .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 09d8eb55..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)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDataDocumentReader.cs deleted file mode 100644 index 16e4ade5..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) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocument.cs deleted file mode 100644 index bf63cb44..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.Transactions.Steps; -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 static InsertDocumentsCommand GetInsert - ( - IEnvelopeService envelopeService, - ITransaction transaction, - IAppendCommandTransactionStep appendCommandTransactionStep - ) - { - return new InsertDocumentsCommand - ( - TableName, - new[] - { - new CommandDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = appendCommandTransactionStep.EntityId, - EntityVersionNumber = appendCommandTransactionStep.EntityVersionNumber, - DataType = appendCommandTransactionStep.Command.GetType().Name, - Data = envelopeService.Serialize(appendCommandTransactionStep.Command) - } - } - ); - } - - 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 is null - ? default - : document.EntityVersionNumber; - } - - 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 b00efe12..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)), - 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), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Command/CommandDocumentReaderBase.cs deleted file mode 100644 index f62c15c6..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 ea436dbb..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)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandEntityVersionNumberDocumentReader.cs deleted file mode 100644 index 64052723..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))) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Command/CommandTransactionIdDocumentReader.cs deleted file mode 100644 index c7bf96f3..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)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/DocumentBase.cs b/src/EntityDb.SqlDb/Documents/DocumentBase.cs deleted file mode 100644 index 81202498..00000000 --- a/src/EntityDb.SqlDb/Documents/DocumentBase.cs +++ /dev/null @@ -1,12 +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!; -} 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 8d246d03..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) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs deleted file mode 100644 index d6135e94..00000000 --- a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocument.cs +++ /dev/null @@ -1,111 +0,0 @@ -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; -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 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 GetInsert - ( - IEnvelopeService envelopeService, - ITransaction transaction, - IAddLeasesTransactionStep addLeasesTransactionStep - ) - { - return new InsertDocumentsCommand - ( - TableName, - addLeasesTransactionStep.Leases - .Select(insertLease => - { - return new LeaseDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = addLeasesTransactionStep.EntityId, - EntityVersionNumber = addLeasesTransactionStep.EntityVersionNumber, - Scope = insertLease.Scope, - Label = insertLease.Label, - Value = insertLease.Value, - DataType = insertLease.GetType().Name, - Data = envelopeService.Serialize(insertLease) - }; - }) - .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 - ( - IDeleteLeasesTransactionStep deleteLeasesTransactionStep - ) - { - var deleteLeasesQuery = - new DeleteLeasesQuery(deleteLeasesTransactionStep.EntityId, deleteLeasesTransactionStep.Leases); - - 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 08b248b1..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)), - 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), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseDocumentReaderBase.cs deleted file mode 100644 index 6184cd1e..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 2a20b391..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)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Lease/LeaseTransactionIdDocumentReader.cs deleted file mode 100644 index 3aef3b6d..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)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDataDocumentReader.cs deleted file mode 100644 index 595c790b..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) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs deleted file mode 100644 index 9ca3baa2..00000000 --- a/src/EntityDb.SqlDb/Documents/Tag/TagDocument.cs +++ /dev/null @@ -1,107 +0,0 @@ -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; -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 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 GetInsert - ( - IEnvelopeService envelopeService, - ITransaction transaction, - IAddTagsTransactionStep addTagsTransactionStep - ) - { - return new InsertDocumentsCommand - ( - TableName, - addTagsTransactionStep.Tags - .Select(insertTag => - { - return new TagDocument - { - TransactionTimeStamp = transaction.TimeStamp, - TransactionId = transaction.Id, - EntityId = addTagsTransactionStep.EntityId, - EntityVersionNumber = addTagsTransactionStep.EntityVersionNumber, - Label = insertTag.Label, - Value = insertTag.Value, - DataType = insertTag.GetType().Name, - Data = envelopeService.Serialize(insertTag) - }; - }) - .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 - ( - IDeleteTagsTransactionStep deleteTagsTransactionStep - ) - { - var deleteTagsQuery = new DeleteTagsQuery(deleteTagsTransactionStep.EntityId, deleteTagsTransactionStep.Tags); - - 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 c3b88336..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)), - 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), - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs b/src/EntityDb.SqlDb/Documents/Tag/TagDocumentReaderBase.cs deleted file mode 100644 index af3ec606..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 d31e85cd..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)) - }; - } -} diff --git a/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs b/src/EntityDb.SqlDb/Documents/Tag/TagTransactionIdDocumentReader.cs deleted file mode 100644 index 5a394a9c..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)) - }; - } -} 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 86b9968b..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.TransactionId), - 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.EntityId), - 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.EntityIds)), - 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 d392d611..00000000 --- a/src/EntityDb.SqlDb/Queries/FilterBuilders/AgentSignatureFilterBuilder.cs +++ /dev/null @@ -1,19 +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 EntityIdsIn(params Id[] entityIds) - { - return AnyIn(nameof(AgentSignatureDocument.EntityIds), entityIds); - } - - 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 ec587dfd..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 TransactionTimeStampGte(TimeStamp timeStamp) - { - return Gte(nameof(ITransactionDocument.TransactionTimeStamp), timeStamp); - } - - public IFilterDefinition TransactionTimeStampLte(TimeStamp timeStamp) - { - return Lte(nameof(ITransactionDocument.TransactionTimeStamp), timeStamp); - } - - public IFilterDefinition TransactionIdIn(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 86488707..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 TransactionTimeStamp(bool ascending) - { - return Sort(ascending, nameof(ITransactionDocument.TransactionTimeStamp)); - } - - public ISortDefinition TransactionId(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 029229bb..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 c9451ff5..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 = default!; - - 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 filterDefintiion, - ISortDefinition? sortDefinition, - int? skip, - int? limit, - TOptions? options, - [EnumeratorCancellation] CancellationToken cancellationToken - ) - where TDocument : ITransactionDocument - { - var tableName = TDocument.TableName; - - var dbQuery = _sqlConverter.ConvertQuery(tableName, documentReader, filterDefintiion, 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); - - 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/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 922e0987..00000000 --- a/src/EntityDb.SqlDb/Transactions/SqlDbTransactionRepository.cs +++ /dev/null @@ -1,247 +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.Transactions.Steps; -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 transactionStep in transaction.Steps) - { - 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() - }); - } - - 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, IAppendCommandTransactionStep appendCommandTransactionStep, - 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) - .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); - } - - private async Task DeleteLeases(IDeleteLeasesTransactionStep deleteLeasesTransactionStep, - CancellationToken cancellationToken) - { - await LeaseDocument - .GetDeleteCommand(deleteLeasesTransactionStep) - .Execute(_sqlDbSession, cancellationToken); - } - - private async Task DeleteTags(IDeleteTagsTransactionStep deleteTagsTransactionStep, - CancellationToken cancellationToken) - { - await TagDocument - .GetDeleteCommand(deleteTagsTransactionStep) - .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 13ce083c..00000000 --- a/src/EntityDb.Void/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using EntityDb.Abstractions.Transactions; -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/VoidTransactionRepository.cs b/src/EntityDb.Void/Transactions/VoidTransactionRepository.cs deleted file mode 100644 index d1f2d774..00000000 --- a/src/EntityDb.Void/Transactions/VoidTransactionRepository.cs +++ /dev/null @@ -1,102 +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.Void.Transactions; - -internal sealed class VoidTransactionRepository : DisposableResourceBaseClass, ITransactionRepository -{ - public IAsyncEnumerable EnumerateTransactionIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateTransactionIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateTransactionIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateTransactionIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateEntityIds(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateEntityIds(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateEntityIds(ILeaseQuery leaseQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateEntityIds(ITagQuery tagQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateAgentSignatures(IAgentSignatureQuery agentSignatureQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty(); - } - - public IAsyncEnumerable EnumerateCommands(ICommandQuery commandQuery, - 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> EnumerateAnnotatedCommands(ICommandQuery commandQuery, - CancellationToken cancellationToken = default) - { - return AsyncEnumerable.Empty>(); - } - - public Task PutTransaction(ITransaction transaction, - CancellationToken cancellationToken = default) - { - return Task.FromResult(false); - } -} 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/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 0c114044..00000000 Binary files a/test/.DS_Store and /dev/null differ diff --git a/test/Directory.Build.props b/test/Directory.Build.props index dc86de2d..368f913c 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -13,18 +13,18 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive + + 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 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 f7298901..ed6147df 100644 --- a/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs +++ b/test/EntityDb.Common.Tests/DatabaseContainerFixture.cs @@ -1,20 +1,12 @@ -using DotNet.Testcontainers.Builders; -using DotNet.Testcontainers.Configurations; -using DotNet.Testcontainers.Containers; +using Testcontainers.MongoDb; +using Testcontainers.Redis; using Xunit; namespace EntityDb.Common.Tests; -public class DatabaseContainerFixture : IAsyncLifetime +public sealed 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,44 +16,27 @@ 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 MongoDbTestcontainerConfiguration MongoDbConfiguration => _mongoDbConfiguration; - - public PostgreSqlTestcontainerConfiguration PostgreSqlConfiguration => _postgreSqlConfiguration; - - public RedisTestcontainer RedisContainer { get; } = new TestcontainersBuilder() - .WithDatabase(_redisConfiguration) + public RedisContainer RedisContainer { get; } = new RedisBuilder() + .WithImage("redis:7.2.0") .Build(); - public MongoDbTestcontainer MongoDbContainer { get; } = new TestcontainersBuilder() - .WithDatabase(_mongoDbConfiguration) + public MongoDbContainer MongoDbContainer { get; } = new MongoDbBuilder() + .WithImage("mongo:7.0.0") + .WithUsername(null) + .WithPassword(null) .WithBindMount(DockerVolumeMongoDbInit, "/docker-entrypoint-initdb.d") - .WithCommand("--replSet", "entitydb") + .WithCommand("--replSet", OmniParameter) .Build(); - public PostgreSqlTestcontainer PostgreSqlContainer { get; } = new TestcontainersBuilder() - .WithDatabase(_postgreSqlConfiguration) - .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(); } } diff --git a/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs new file mode 100644 index 00000000..30f02af2 --- /dev/null +++ b/test/EntityDb.Common.Tests/Entities/EntityRepositoryTests.cs @@ -0,0 +1,251 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Attributes; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using System.Diagnostics.CodeAnalysis; +using Xunit; + +namespace EntityDb.Common.Tests.Entities; + +[Collection(nameof(DatabaseContainerCollection))] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class EntityRepositoryTests : TestsBase +{ + public EntityRepositoryTests(IServiceProvider serviceProvider, DatabaseContainerFixture databaseContainerFixture) : + base( + serviceProvider, databaseContainerFixture) + { + } + + private async Task Generic_GivenEntityNotKnown_WhenGettingEntity_ThenThrow( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : IEntity + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + var entityRepository = await GetReadOnlyEntityRepository(serviceScope, false); + + // ASSERT + + Should.Throw(() => entityRepository.Get(default)); + } + + private async Task Generic_GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : IEntity + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.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( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : IEntity + { + // ARRANGE + + var committedSources = new List(); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); + + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + + // ACT + + writeRepository.Create(default); + + writeRepository.Append(default, new AddLease(new Lease(default!, default!, default!))); + + await writeRepository.Commit(); + + // ASSERT + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].AddLeases.Length.ShouldBe(1); + } + + private async Task Generic_GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : IEntity + { + // ARRANGE + + var committedSources = new List(); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); + + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + + const int numberOfVersionsToTest = 10; + + writeRepository.Create(default); + + // ACT + + for (var i = 1; i <= numberOfVersionsToTest; i++) + { + writeRepository.Append(default, new DoNothing()); + } + + await writeRepository.Commit(); + + // ASSERT + + committedSources.Count.ShouldBe(1); + + var expectedVersion = StateVersion.Zero; + + for (var i = 1; i <= numberOfVersionsToTest; i++) + { + expectedVersion = expectedVersion.Next(); + + committedSources[0].Messages[i - 1].StatePointer.StateVersion.ShouldBe(expectedVersion); + } + } + + private async Task Generic_GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : IEntity + { + // ARRANGE + + var committedSources = new List(); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + serviceCollection.AddSingleton(GetMockedSourceSubscriber(committedSources)); + + 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(); + + // ASSERT + + committedSources.Count.ShouldBe(1); + committedSources[0].Messages.Length.ShouldBe(1); + committedSources[0].Messages[0].Delta.ShouldBeEquivalentTo(new DoNothing()); + } + + [Theory] + [MemberData(nameof(With_Source_Entity))] + public Task GivenEntityNotKnown_WhenGettingEntity_ThenThrow(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } + ); + } + + [Theory] + [MemberData(nameof(With_Source_Entity))] + public Task GivenEntityKnown_WhenGettingEntity_ThenReturnExpectedEntity(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } + ); + } + + [Theory] + [MemberData(nameof(With_Source_Entity))] + public Task GivenAddLeasesDelta_WhenBuildingNewEntityWithLease_ThenSourceDoesAddLeases( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } + ); + } + + [Theory] + [MemberData(nameof(With_Source_Entity))] + public Task GivenNonExistingEntityId_WhenAppendingDeltas_ThenVersionAutoIncrements( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } + ); + } + + [Theory] + [MemberData(nameof(With_Source_Entity))] + public Task GivenExistingEntity_WhenAppendingNewDelta_ThenSourceBuilds(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityRepositoryAdder.EntityType }, + new object?[] { sourceRepositoryAdder, entityRepositoryAdder } + ); + } +} diff --git a/test/EntityDb.Common.Tests/Entities/EntityTests.cs b/test/EntityDb.Common.Tests/Entities/EntityTests.cs index 3e6bb5cb..bad8e535 100644 --- a/test/EntityDb.Common.Tests/Entities/EntityTests.cs +++ b/test/EntityDb.Common.Tests/Entities/EntityTests.cs @@ -1,410 +1,428 @@ -using EntityDb.Abstractions.Entities; -using EntityDb.Abstractions.Queries; -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.Abstractions; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; using EntityDb.Common.Exceptions; using EntityDb.Common.Polyfills; -using EntityDb.Common.Tests.Implementations.Commands; -using EntityDb.Common.Tests.Implementations.Snapshots; +using EntityDb.Common.Sources.Agents; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; +using EntityDb.Common.Tests.Implementations.Seeders; +using EntityDb.Common.Tests.Implementations.States; using Microsoft.Extensions.DependencyInjection; using Moq; using Shouldly; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace EntityDb.Common.Tests.Entities; [Collection(nameof(DatabaseContainerCollection))] -public class EntityTests : TestsBase +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed 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 - ( - IServiceScope serviceScope, - Id entityId, - VersionNumber from, - VersionNumber to, - TEntity? entity = default - ) - where TEntity : class, IEntity, ISnapshotWithTestLogic + private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) + where TEntity : class, IEntity { - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, entityId); - - if (entity is not null) transactionBuilder.Load(entity); + // ARRANGE - for (var i = from; i.Value <= to.Value; i = i.Next()) + await using var serviceScope = CreateServiceScope(serviceCollection => { - if (transactionBuilder.IsEntityKnown() && - transactionBuilder.GetEntity().VersionNumber.Value >= i.Value) continue; - - transactionBuilder.Append(new DoNothing()); - } - - return transactionBuilder.Build(Id.NewId()); - } - + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); - private async Task Generic_GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE + 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 versionNumberN = new VersionNumber(n); - - var versionNumberM = new VersionNumber(m); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); - }); + var versionM = new StateVersion(m); var entityId = Id.NewId(); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write, - TestSessionOptions.Write); + writeRepository.Create(entityId); + writeRepository.Seed(entityId, m); + writeRepository.Seed(entityId, n - m); - var transaction = await BuildTransaction(serviceScope, entityId, new VersionNumber(1), versionNumberN); - - var transactionInserted = await entityRepository.PutTransaction(transaction); + var committed = await writeRepository.Commit(); // ARRANGE ASSERTIONS - transactionInserted.ShouldBeTrue(); + committed.ShouldBeTrue(); // ACT - var entityAtVersionM = await entityRepository.GetSnapshot(entityId + versionNumberM); + await readOnlyRepository.TryLoad(entityId + new StateVersion(m)); + + var entity = readOnlyRepository.Get(entityId); // ASSERT - entityAtVersionM.VersionNumber.ShouldBe(versionNumberM); + entity.GetPointer().StateVersion.ShouldBe(versionM); } - private async Task Generic_GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetCommandsRuns( - EntityAdder entityAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic + private async Task Generic_GivenExistingEntityWithNoPersistedState_WhenGettingEntity_ThenGetDeltasRuns( + EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity { // ARRANGE - var expectedVersionNumber = new VersionNumber(10); + const uint expectedVersionNumber = 10; + var expectedVersion = new StateVersion(expectedVersionNumber); 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 transactionStep in transaction.Steps) - if (transactionStep is IAppendCommandTransactionStep commandTransactionStep) - commands.Add(commandTransactionStep.Command); + 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 - .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) - .ReturnsAsync(transactionRepositoryMock.Object); + sourceRepositoryFactoryMock + .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(transactionRepositoryFactoryMock.Object); + serviceCollection.AddSingleton(sourceRepositoryFactoryMock.Object); }); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + await using var writeRepository = await GetWriteEntityRepository(serviceScope, false); + await using var readOnlyRepository = await GetReadOnlyEntityRepository(serviceScope, false); - var transaction = - await BuildTransaction(serviceScope, entityId, new VersionNumber(1), expectedVersionNumber); + writeRepository.Create(entityId); + writeRepository.Seed(entityId, expectedVersionNumber); - var transactionInserted = await entityRepository.PutTransaction(transaction); + var committed = await writeRepository.Commit(); // ARRANGE ASSERTIONS - transactionInserted.ShouldBeTrue(); + committed.ShouldBeTrue(); // ACT - var currenEntity = await entityRepository.GetSnapshot(entityId); + await readOnlyRepository.TryLoad(entityId); + + var entity = readOnlyRepository.Get(entityId); // ASSERT - currenEntity.VersionNumber.ShouldBe(expectedVersionNumber); + entity.GetPointer().StateVersion.ShouldBe(expectedVersion); - transactionRepositoryMock - .Verify(repository => repository.EnumerateCommands(It.IsAny(), It.IsAny()), + sourceRepositoryMock + .Verify( + 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(GetMockedTransactionRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); }); // ACT - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetService>(); + await using var stateRepositoryFactory = 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 - 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(GetMockedTransactionRepositoryFactory()); - serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedStateRepositoryFactory()); }); // ACT - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetService>(); + await using var stateRepositoryFactory = serviceScope.ServiceProvider + .GetService>(); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository("NOT NULL"); + 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(GetMockedTransactionRepositoryFactory()); - serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory()); + serviceCollection.AddSingleton(GetMockedStateRepositoryFactory()); }); // ACT - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetService>(); + await using var stateRepositoryFactory = 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 - snapshotRepositoryFactory.ShouldNotBeNull(); + stateRepositoryFactory.ShouldNotBeNull(); - entityRepository.SnapshotRepository.ShouldNotBeNull(); + entityRepository.StateRepository.ShouldNotBeNull(); } private async Task - Generic_GivenSnapshotAndNewCommands_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).WithVersionNumber(new VersionNumber(1)); + var previousEntity = TEntity.Construct(default(Id) + new StateVersion(1)); - var newCommands = 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(GetMockedTransactionRepositoryFactory(newCommands)); - serviceCollection.AddSingleton(GetMockedSnapshotRepositoryFactory(snapshot)); + serviceCollection.AddSingleton(GetMockedSourceRepositoryFactory(newDeltas)); + serviceCollection.AddSingleton(GetMockedStateRepositoryFactory(previousEntity)); }); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + // ACT - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository("NOT NULL", "NOT NULL"); + await entityRepository.TryLoad(default); - var snapshotOrDefault = await entityRepository.GetSnapshot(default); + var currentEntity = entityRepository.Get(default); // ASSERT - snapshotOrDefault.ShouldNotBe(default); - snapshotOrDefault.ShouldNotBe(snapshot); - snapshotOrDefault.VersionNumber.ShouldBe( - new VersionNumber(snapshot.VersionNumber.Value + Convert.ToUInt64(newCommands.Length))); + currentEntity.ShouldNotBe(previousEntity); + currentEntity.GetPointer().StateVersion.ShouldBe( + new StateVersion(previousEntity.GetPointer().StateVersion.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(GetMockedTransactionRepositoryFactory()); + 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( + SourceRepositoryAdder sourceRepositoryAdder, EntityRepositoryAdder entityRepositoryAdder) + where TEntity : class, IEntity + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => { - await entityRepository.GetSnapshot(default); + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityRepositoryAdder.AddDependencies.Invoke(serviceCollection); }); + + var entityId = Id.NewId(); + + var expectedEntity = TEntity.Construct(entityId + new StateVersion(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(), StatePointer = entityId, Delta = new DoNothing() } }, + }; + + var committed = await entityRepository.SourceRepository.Commit(source); + + // ARRANGE ASSERTIONS + + committed.ShouldBeTrue(); + + // ACT + + await entityRepository.TryLoad(entityId); + + var actualEntity = entityRepository.Get(entityId); + + // ASSERT + + actualEntity.ShouldBeEquivalentTo(expectedEntity); } [Theory] - [MemberData(nameof(AddTransactionsAndEntitySnapshots))] - public Task GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM(TransactionsAdder transactionsAdder, - SnapshotAdder entitySnapshotAdder) + [MemberData(nameof(With_Source_EntityState))] + public Task GivenEntityWithNVersions_WhenGettingAtVersionM_ThenReturnAtVersionM( + SourceRepositoryAdder sourceRepositoryAdder, + StateRepositoryAdder entityStateRepositoryAdder) { return RunGenericTestAsync ( - new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder } + new[] { entityStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenExistingEntityWithNoSnapshot_WhenGettingEntity_ThenGetCommandsRuns(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[] { entityRepositoryAdder.EntityType }, + new object?[] { entityRepositoryAdder } + ); + } + + [Theory] + [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 GivenSnapshotAndNewCommands_WhenGettingSnapshotOrDefault_ThenReturnNewerThanSnapshot( - 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(AddEntity))] - public Task GivenNonExistentEntityId_WhenGettingCurrentEntity_ThenThrow(EntityAdder entityAdder) + [MemberData(nameof(With_Source_Entity))] + public Task GivenEntityCommitted_WhenGettingEntity_ThenReturnEntity(SourceRepositoryAdder sourceRepositoryAdder, + EntityRepositoryAdder entityRepositoryAdder) { return RunGenericTestAsync ( - new[] { entityAdder.EntityType }, - new object?[] { 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 47634e17..6d38424f 100644 --- a/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj +++ b/test/EntityDb.Common.Tests/EntityDb.Common.Tests.csproj @@ -1,32 +1,21 @@  - - - - - + + + - - - - + + + + - - - Always - - + + + Always + + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - 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/Extensions/FilterBuilderExtensions.cs b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs similarity index 84% rename from test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensions.cs rename to test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs index 45f315bf..4b4534be 100644 --- a/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensions.cs +++ b/test/EntityDb.Common.Tests/Extensions/FilterBuilderExtensionsTests.cs @@ -1,16 +1,16 @@ -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; namespace EntityDb.Common.Tests.Extensions; -public class FilterBuilderExtensionsTests +public sealed 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())) @@ -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(); @@ -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); -} \ No newline at end of file + private sealed record DoubleInput(bool Input1, bool Input2, bool ExpectedOutput); +} 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/Commands/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs deleted file mode 100644 index 68db22cc..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Commands/DoNothing.cs +++ /dev/null @@ -1,21 +0,0 @@ -using EntityDb.Abstractions.Reducers; -using EntityDb.Common.Tests.Implementations.Entities; -using EntityDb.Common.Tests.Implementations.Projections; - -namespace EntityDb.Common.Tests.Implementations.Commands; - -public record DoNothing : IReducer, IReducer -{ - public OneToOneProjection Reduce(OneToOneProjection projection) - { - return projection with - { - VersionNumber = projection.VersionNumber.Next() - }; - } - - public TestEntity Reduce(TestEntity entity) - { - return entity with { VersionNumber = entity.VersionNumber.Next() }; - } -} \ 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 51423dc6..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Commands/StoreNumber.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EntityDb.Abstractions.Reducers; -using EntityDb.Common.Tests.Implementations.Entities; -using EntityDb.Common.Tests.Implementations.Projections; - -namespace EntityDb.Common.Tests.Implementations.Commands; - -public record StoreNumber(ulong Number) : IReducer, IReducer -{ - public OneToOneProjection Reduce(OneToOneProjection projection) - { - return projection with - { - VersionNumber = projection.VersionNumber.Next() - }; - } - - public TestEntity Reduce(TestEntity entity) - { - return entity with - { - VersionNumber = entity.VersionNumber.Next() - }; - } -} \ 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 97ac65dc..00000000 --- a/test/EntityDb.Common.Tests/Implementations/DbContexts/GenericDbContext.cs +++ /dev/null @@ -1,55 +0,0 @@ -using EntityDb.Common.Tests.Implementations.Snapshots; -using EntityDb.EntityFramework.DbContexts; -using EntityDb.EntityFramework.Sessions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace EntityDb.Common.Tests.Implementations.DbContexts; - -internal class GenericDbContext : EntityDbContextBase, IEntityDbContext> - where TSnapshot : class, ISnapshotWithTestLogic -{ - 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 GenericDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions) - { - return new GenericDbContext(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/Entities/Deltas/AddLease.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs new file mode 100644 index 00000000..1197b007 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddLease.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public sealed 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..d16c388e --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/AddTag.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public sealed 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..f441c992 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteLease.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public sealed 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..79967902 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DeleteTag.cs @@ -0,0 +1,12 @@ +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.States.Deltas; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public sealed record DeleteTag(ITag Tag) : DoNothing, IDeleteTagsDelta +{ + public IEnumerable GetTags(TestEntity state) + { + yield return Tag; + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs new file mode 100644 index 00000000..caf86358 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/DoNothing.cs @@ -0,0 +1,17 @@ +using EntityDb.Abstractions.States.Transforms; +using EntityDb.Common.Tests.Implementations.Projections; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public record DoNothing : IReducer, IMutator +{ + public void Mutate(OneToOneProjection projection) + { + projection.StatePointer = projection.StatePointer.Next(); + } + + public TestEntity Reduce(TestEntity entity) + { + 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 new file mode 100644 index 00000000..19bb62fc --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Entities/Deltas/StoreNumber.cs @@ -0,0 +1,31 @@ +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.Sources.Attributes; + +namespace EntityDb.Common.Tests.Implementations.Entities.Deltas; + +public sealed record StoreNumber(ulong Number) : IReducer, IMutator, + IAddLeasesDelta, IAddTagsDelta +{ + public IEnumerable GetLeases(TestEntity state) + { + yield return new CountLease(Number); + } + + public IEnumerable GetTags(TestEntity state) + { + yield return new CountTag(Number); + } + + public void Mutate(OneToOneProjection projection) + { + projection.StatePointer = projection.StatePointer.Next(); + } + + public TestEntity Reduce(TestEntity entity) + { + 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 759f9337..2f7c472f 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,78 +1,54 @@ -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 Microsoft.EntityFrameworkCore.Metadata.Builders; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.States.Transforms; +using EntityDb.Common.Tests.Implementations.States; namespace EntityDb.Common.Tests.Implementations.Entities; -public record TestEntity : IEntity, ISnapshotWithTestLogic +public sealed record TestEntity : IEntity, IStateWithTestLogic { - public required Id Id { get; init; } - public VersionNumber VersionNumber { get; init; } + public required StatePointer StatePointer { get; init; } - public static TestEntity Construct(Id entityId) + public static TestEntity Construct(StatePointer statePointer) { - return new TestEntity - { - Id = entityId, - }; - } - - public static void Configure(EntityTypeBuilder testEntityBuilder) - { - testEntityBuilder - .HasKey(testEntity => new - { - testEntity.Id, - testEntity.VersionNumber, - }); + return new TestEntity { StatePointer = statePointer }; } - public Id GetId() + public StatePointer GetPointer() { - return Id; + return StatePointer; } - public VersionNumber GetVersionNumber() + public static bool CanReduce(object delta) { - return VersionNumber; + return delta is IReducer; } - public TestEntity Reduce(object command) + public TestEntity Reduce(object delta) { - return command switch + if (delta is IReducer reducer) { - DoNothing doNothing => doNothing.Reduce(this), - StoreNumber storeNumber => storeNumber.Reduce(this), - _ => throw new NotSupportedException() - }; - } + return reducer.Reduce(this); + } - public bool ShouldRecord() - { - return ShouldRecordLogic.Value is not null && ShouldRecordLogic.Value.Invoke(this); + throw new NotSupportedException(); } - public bool ShouldRecordAsLatest(TestEntity? previousMostRecentSnapshot) + public bool ShouldPersist() { - return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousMostRecentSnapshot); + return ShouldPersistLogic.Value is not null && ShouldPersistLogic.Value.Invoke(this); } - public static string RedisKeyNamespace => "test-entity"; - - public TestEntity WithVersionNumber(VersionNumber versionNumber) + public bool ShouldPersistAsLatest() { - return this with { VersionNumber = versionNumber }; + return ShouldPersistAsLatestLogic.Value is not null && + ShouldPersistAsLatestLogic.Value.Invoke(this); } - public static AsyncLocal?> ShouldRecordLogic { get; } = new(); + public static string MongoDbCollectionName => "TestEntities"; + public static string RedisKeyNamespace => "test-entity"; - public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = new(); + public static AsyncLocal?> ShouldPersistLogic { get; } = new(); - public Expression> GetKeyPredicate() - { - return (testEntity) => testEntity.Id == Id && testEntity.VersionNumber == VersionNumber; - } -} \ No newline at end of file + public static AsyncLocal?> ShouldPersistAsLatestLogic { get; } = new(); +} diff --git a/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs b/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs deleted file mode 100644 index 442d23cd..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Leases/CountLease.cs +++ /dev/null @@ -1,10 +0,0 @@ -using EntityDb.Abstractions.Leases; - -namespace EntityDb.Common.Tests.Implementations.Leases; - -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/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index a3914809..76f00fb3 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,100 +1,96 @@ -using System.Linq.Expressions; -using EntityDb.Abstractions.Annotations; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Reducers; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Projections; -using EntityDb.Common.Queries; -using EntityDb.Common.Tests.Implementations.Entities; -using EntityDb.Common.Tests.Implementations.Snapshots; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; +using EntityDb.Abstractions; +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.States.Transforms; +using EntityDb.Common.Sources; +using EntityDb.Common.Sources.Queries.Standard; +using EntityDb.Common.Tests.Implementations.States; +using Microsoft.Extensions.DependencyInjection; +using System.Runtime.CompilerServices; namespace EntityDb.Common.Tests.Implementations.Projections; -public record OneToOneProjection : IProjection, ISnapshotWithTestLogic +public sealed class OneToOneProjection : IProjection, IStateWithTestLogic { - public required Id Id { get; init; } - public TimeStamp LastEventAt { get; init; } - public VersionNumber VersionNumber { get; init; } + public TimeStamp LastSourceAt { get; set; } - public static OneToOneProjection Construct(Id projectionId) - { - return new OneToOneProjection - { - Id = projectionId, - }; - } + public required StatePointer StatePointer { get; set; } - public static void Configure(EntityTypeBuilder oneToOneProjectionBuilder) + public static OneToOneProjection Construct(StatePointer statePointer) { - oneToOneProjectionBuilder - .HasKey(oneToOneProjection => new - { - oneToOneProjection.Id, - oneToOneProjection.VersionNumber, - }); + return new OneToOneProjection { StatePointer = statePointer }; } - public Id GetId() + public StatePointer GetPointer() { - return Id; + return StatePointer; } - public VersionNumber GetVersionNumber() + public void Mutate(Source source) { - return VersionNumber; - } + LastSourceAt = source.TimeStamp; - public OneToOneProjection Reduce(IEntityAnnotation annotatedCommand) - { - return annotatedCommand switch + foreach (var message in source.Messages) { - IEntityAnnotation> reducer => reducer.Data.Reduce(this) with + if (message.Delta is not IMutator mutator) { - LastEventAt = reducer.TransactionTimeStamp, - VersionNumber = reducer.EntityVersionNumber - }, - _ => throw new NotSupportedException() - }; - } + continue; + } - public bool ShouldRecord() - { - return ShouldRecordLogic.Value is not null && ShouldRecordLogic.Value.Invoke(this); + mutator.Mutate(this); + + StatePointer = message.StatePointer; + } } - public bool ShouldRecordAsLatest(OneToOneProjection? previousSnapshot) + public bool ShouldPersist() { - return ShouldRecordAsLatestLogic.Value is not null && ShouldRecordAsLatestLogic.Value.Invoke(this, previousSnapshot); + return ShouldPersistLogic.Value is not null && ShouldPersistLogic.Value.Invoke(this); } - public ICommandQuery GetCommandQuery(Pointer projectionPointer) + public bool ShouldPersistAsLatest() { - return new GetEntityCommandsQuery(projectionPointer, VersionNumber); + return ShouldPersistAsLatestLogic.Value is not null && + ShouldPersistAsLatestLogic.Value.Invoke(this); } - public static Id? GetProjectionIdOrDefault(object entity) + public async IAsyncEnumerable EnumerateSources + ( + IServiceProvider serviceProvider, + StatePointer projectionPointer, + [EnumeratorCancellation] CancellationToken cancellationToken + ) { - if (entity is TestEntity testEntity) return testEntity.Id; + var query = new GetDeltasDataQuery(projectionPointer, default); - return null; - } + var sourceRepository = await serviceProvider + .GetRequiredService() + .Create(TestSessionOptions.ReadOnly, cancellationToken); - public static string RedisKeyNamespace => "one-to-one-projection"; + var sourceIds = await sourceRepository + .EnumerateSourceIds(query, cancellationToken) + .ToArrayAsync(cancellationToken); - public OneToOneProjection WithVersionNumber(VersionNumber versionNumber) + foreach (var sourceId in sourceIds) + { + yield return await sourceRepository + .GetSource(sourceId, default, cancellationToken); + } + } + + public static IEnumerable EnumerateProjectionIds(Source source) { - return this with { VersionNumber = versionNumber }; + return source.Messages + .Where(message => message.Delta is IMutator) + .Select(message => message.StatePointer.Id); } - public static AsyncLocal?> ShouldRecordLogic { get; } = new(); + public static string MongoDbCollectionName => "OneToOneProjections"; + public static string RedisKeyNamespace => "one-to-one-projection"; - public static AsyncLocal?> ShouldRecordAsLatestLogic { get; } = - new(); + public static AsyncLocal?> ShouldPersistLogic { get; } = new(); - public Expression> GetKeyPredicate() - { - return (oneToOneProjection) => oneToOneProjection.Id == Id && oneToOneProjection.VersionNumber == VersionNumber; - } -} \ No newline at end of file + public static AsyncLocal?> ShouldPersistAsLatestLogic { get; } = + new(); +} diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs deleted file mode 100644 index 36fe9117..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Queries/EntityIdQuery.cs +++ /dev/null @@ -1,65 +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 EntityIdQuery(Id EntityId, object? Options = null) : IAgentSignatureQuery, ICommandQuery, ILeaseQuery, ITagQuery -{ - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) - { - return builder.EntityIdsIn(EntityId); - } - - 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); - } - - public TSort GetSort(ICommandSortBuilder builder) - { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } - - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(ILeaseSortBuilder builder) - { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } - - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.EntityIdIn(EntityId); - } - - public TSort GetSort(ITagSortBuilder builder) - { - return builder.Combine - ( - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } -} \ 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/TransactionIdQuery.cs b/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs deleted file mode 100644 index 484b7615..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionIdQuery.cs +++ /dev/null @@ -1,72 +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 TransactionIdQuery(Id TransactionId, object? Options = null) : IAgentSignatureQuery, ICommandQuery, ILeaseQuery, - ITagQuery -{ - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) - { - return builder.TransactionIdIn(TransactionId); - } - - public TSort GetSort(IAgentSignatureSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionId(true) - ); - } - - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(ICommandFilterBuilder builder) - { - return builder.TransactionIdIn(TransactionId); - } - - public TSort GetSort(ICommandSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionId(true), - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } - - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.TransactionIdIn(TransactionId); - } - - public TSort GetSort(ILeaseSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionId(true), - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } - - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.TransactionIdIn(TransactionId); - } - - public TSort GetSort(ITagSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionId(true), - builder.EntityId(true), - builder.EntityVersionNumber(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/TransactionTimeStampQuery.cs deleted file mode 100644 index 0f407040..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Queries/TransactionTimeStampQuery.cs +++ /dev/null @@ -1,88 +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 TransactionTimeStampQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : IAgentSignatureQuery, ICommandQuery, - ILeaseQuery, ITagQuery -{ - public TFilter GetFilter(IAgentSignatureFilterBuilder builder) - { - return builder.And - ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) - ); - } - - public TSort GetSort(IAgentSignatureSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionTimeStamp(true) - ); - } - - public int? Skip => null; - - public int? Take => null; - - public TFilter GetFilter(ICommandFilterBuilder builder) - { - return builder.And - ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) - ); - } - - public TSort GetSort(ICommandSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionTimeStamp(true), - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } - - public TFilter GetFilter(ILeaseFilterBuilder builder) - { - return builder.And - ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) - ); - } - - public TSort GetSort(ILeaseSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionTimeStamp(true), - builder.EntityId(true), - builder.EntityVersionNumber(true) - ); - } - - public TFilter GetFilter(ITagFilterBuilder builder) - { - return builder.And - ( - builder.TransactionTimeStampGte(Gte), - builder.TransactionTimeStampLte(Lte) - ); - } - - public TSort GetSort(ITagSortBuilder builder) - { - return builder.Combine - ( - builder.TransactionTimeStamp(true), - builder.EntityId(true), - builder.EntityVersionNumber(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/CommandSeeder.cs deleted file mode 100644 index 2594b065..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/CommandSeeder.cs +++ /dev/null @@ -1,11 +0,0 @@ -using EntityDb.Common.Tests.Implementations.Commands; - -namespace EntityDb.Common.Tests.Implementations.Seeders; - -public static class CommandSeeder -{ - 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 new file mode 100644 index 00000000..f4318133 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Seeders/EntityRepositoryExtensions.cs @@ -0,0 +1,22 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; +using EntityDb.Common.Tests.Implementations.Entities.Deltas; + +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 + { + for (ulong i = 0; i < numDeltas; i++) + { + entityRepository.Append(entityId, new DoNothing()); + } + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/LeaseSeeder.cs index 0c32b7de..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.Leases; -using EntityDb.Common.Leases; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Sources.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/TagSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TagSeeder.cs index 2fc37efa..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.Tags; -using EntityDb.Common.Tags; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Common.Sources.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/Seeders/TransactionSeeder.cs b/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs deleted file mode 100644 index 93c036b2..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Seeders/TransactionSeeder.cs +++ /dev/null @@ -1,56 +0,0 @@ -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) - { - return new Transaction - { - Id = Id.NewId(), - TimeStamp = TimeStamp.UtcNow, - AgentSignature = new UnknownAgentSignature(new Dictionary()), - Steps = transactionSteps.ToImmutableArray() - }; - } - - public static ITransaction Create(Id entityId, uint numCommands) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - var transactionSteps = TransactionStepSeeder.CreateFromCommands(entityId, numCommands).ToArray(); - - return Create(transactionSteps); - } -} \ 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 72b2a309..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Snapshots/ISnapshotWithTestLogic.cs +++ /dev/null @@ -1,17 +0,0 @@ -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, 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(EntityTypeBuilder snapshotBuilder); - TSnapshot WithVersionNumber(VersionNumber versionNumber); -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountLease.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountLease.cs new file mode 100644 index 00000000..39d744ff --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountLease.cs @@ -0,0 +1,10 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Common.Tests.Implementations.Sources.Attributes; + +public sealed record CountLease(ulong Number) : ILease +{ + public string Scope => ""; + public string Label => ""; + public string Value => $"{Number}"; +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountTag.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountTag.cs new file mode 100644 index 00000000..ad2f5eb7 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Attributes/CountTag.cs @@ -0,0 +1,9 @@ +using EntityDb.Abstractions.Sources.Attributes; + +namespace EntityDb.Common.Tests.Implementations.Sources.Attributes; + +public sealed record CountTag(ulong Number) : ITag +{ + public string Label => ""; + public string Value => $"{Number}"; +} diff --git a/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs similarity index 52% rename from test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs rename to test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs index 09d9713c..4fac6d00 100644 --- a/test/EntityDb.Common.Tests/Implementations/Queries/CountQuery.cs +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/CountDataQuery.cs @@ -1,22 +1,21 @@ -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Queries.FilterBuilders; -using EntityDb.Abstractions.Queries.SortBuilders; -using EntityDb.Common.Tests.Implementations.Leases; -using EntityDb.Common.Tests.Implementations.Tags; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Common.Tests.Implementations.Sources.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 +public sealed record CountDataQuery(ulong Gte, ulong Lte, object? Options = null) : ILeaseDataQuery, ITagDataQuery { public int? Skip => null; public int? Take => null; - public TFilter GetFilter(ILeaseFilterBuilder builder) + public TFilter GetFilter(ILeaseDataFilterBuilder builder) { return builder.And ( - builder.LeaseTypeIn(typeof(CountLease)), + builder.DataTypeIn(typeof(CountLease)), builder.Or ( Enumerable @@ -27,7 +26,7 @@ public TFilter GetFilter(ILeaseFilterBuilder builder) ); } - public TSort GetSort(ILeaseSortBuilder builder) + public TSort GetSort(ILeaseDataSortBuilder builder) { return builder.Combine ( @@ -35,11 +34,11 @@ public TSort GetSort(ILeaseSortBuilder builder) ); } - public TFilter GetFilter(ITagFilterBuilder builder) + public TFilter GetFilter(ITagDataFilterBuilder builder) { return builder.And ( - builder.TagTypeIn(typeof(CountTag)), + builder.DataTypeIn(typeof(CountTag)), builder.Or ( Enumerable @@ -50,11 +49,11 @@ public TFilter GetFilter(ITagFilterBuilder builder) ); } - public TSort GetSort(ITagSortBuilder builder) + public TSort GetSort(ITagDataSortBuilder builder) { return builder.Combine ( builder.TagValue(true) ); } -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs new file mode 100644 index 00000000..f5cc075b --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceIdDataQuery.cs @@ -0,0 +1,73 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; + +public sealed record SourceIdDataQuery(Id SourceId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, + ILeaseDataQuery, + ITagDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + public TSort GetSort(ILeaseDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceId(true), + builder.StateId(true), + builder.StateVersion(true) + ); + } + + public TFilter GetFilter(IMessageDataFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + public TSort GetSort(IMessageDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceId(true), + builder.StateId(true), + builder.StateVersion(true) + ); + } + + public TFilter GetFilter(ISourceDataFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + public TSort GetSort(ISourceDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceId(true) + ); + } + + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagDataFilterBuilder builder) + { + return builder.SourceIdIn(SourceId); + } + + public TSort GetSort(ITagDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceId(true), + builder.StateId(true), + builder.StateVersion(true) + ); + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs new file mode 100644 index 00000000..f44f1896 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/SourceTimeStampDataQuery.cs @@ -0,0 +1,89 @@ +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; + +public sealed record SourceTimeStampDataQuery(TimeStamp Gte, TimeStamp Lte, object? Options = null) : ISourceDataQuery, + IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.And + ( + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) + ); + } + + public TSort GetSort(ILeaseDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceTimeStamp(true), + builder.StateId(true), + builder.StateVersion(true) + ); + } + + public TFilter GetFilter(IMessageDataFilterBuilder builder) + { + return builder.And + ( + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) + ); + } + + public TSort GetSort(IMessageDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceTimeStamp(true), + builder.StateId(true), + builder.StateVersion(true) + ); + } + + public TFilter GetFilter(ISourceDataFilterBuilder builder) + { + return builder.And + ( + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) + ); + } + + public TSort GetSort(ISourceDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceTimeStamp(true) + ); + } + + public int? Skip => null; + + public int? Take => null; + + public TFilter GetFilter(ITagDataFilterBuilder builder) + { + return builder.And + ( + builder.SourceTimeStampGte(Gte), + builder.SourceTimeStampLte(Lte) + ); + } + + public TSort GetSort(ITagDataSortBuilder builder) + { + return builder.Combine + ( + builder.SourceTimeStamp(true), + builder.StateId(true), + builder.StateVersion(true) + ); + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs new file mode 100644 index 00000000..ecd1f036 --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateDataQuery.cs @@ -0,0 +1,66 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; + +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; + +public sealed record StateDataQuery(Id StateId, object? Options = null) : ISourceDataQuery, IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.StateIdIn(StateId); + } + + public TSort GetSort(ILeaseDataSortBuilder 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(ITagDataSortBuilder builder) + { + return builder.Combine + ( + builder.StateId(true), + builder.StateVersion(true) + ); + } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs new file mode 100644 index 00000000..8e9ac2ae --- /dev/null +++ b/test/EntityDb.Common.Tests/Implementations/Sources/Queries/StateVersionDataQuery.cs @@ -0,0 +1,57 @@ +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.Sources.Queries.FilterBuilders; +using EntityDb.Abstractions.Sources.Queries.SortBuilders; +using EntityDb.Abstractions.States; + +namespace EntityDb.Common.Tests.Implementations.Sources.Queries; + +public sealed record StateVersionDataQuery(StateVersion Gte, StateVersion Lte, object? Options = null) : + IMessageDataQuery, + ILeaseDataQuery, ITagDataQuery +{ + public TFilter GetFilter(ILeaseDataFilterBuilder builder) + { + return builder.And + ( + builder.StateVersionGte(Gte), + builder.StateVersionLte(Lte) + ); + } + + public TSort GetSort(ILeaseDataSortBuilder 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(ITagDataSortBuilder builder) + { + return builder.StateVersion(true); + } +} 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..7356bea6 --- /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?> ShouldPersistLogic { get; } + static abstract AsyncLocal?> ShouldPersistAsLatestLogic { get; } +} diff --git a/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs b/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs deleted file mode 100644 index b6030c5a..00000000 --- a/test/EntityDb.Common.Tests/Implementations/Tags/CountTag.cs +++ /dev/null @@ -1,9 +0,0 @@ -using EntityDb.Abstractions.Tags; - -namespace EntityDb.Common.Tests.Implementations.Tags; - -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/Projections/ProjectionsTests.cs b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs index a405c8b5..41e47f3f 100644 --- a/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs +++ b/test/EntityDb.Common.Tests/Projections/ProjectionsTests.cs @@ -1,126 +1,132 @@ +using EntityDb.Abstractions; using EntityDb.Abstractions.Entities; using EntityDb.Abstractions.Projections; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; +using EntityDb.Abstractions.States; 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 EntityDb.Common.Tests.Implementations.States; using Shouldly; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace EntityDb.Common.Tests.Projections; [Collection(nameof(DatabaseContainerCollection))] -public class ProjectionsTests : TestsBase +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed 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_WhenLoadingProjection_ThenReturnFalse( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, + StateRepositoryAdder projectionStateRepositoryAdder) where TProjection : IProjection { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { - transactionsAdder.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 serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write, TestSessionOptions.Write); + await using var readOnlyRepository = await GetReadOnlyProjectionRepository(serviceScope, true); - // ACT & ASSERT + // ACT - await Should.ThrowAsync(() => - projectionRepository.GetSnapshot(default)); - } + var loaded = await readOnlyRepository.TryLoad(default); + + // ASSERT + loaded.ShouldBeFalse(); + } private async Task - Generic_GivenTransactionCommitted_WhenGettingProjection_ThenReturnExpectedProjection( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder, - SnapshotAdder projectionSnapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - where TProjection : class, IProjection, ISnapshotWithTestLogic + Generic_GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, + StateRepositoryAdder projectionStateRepositoryAdder) + where TEntity : class, IEntity, IStateWithTestLogic + where TProjection : class, IProjection, IStateWithTestLogic { // ARRANGE - const uint numberOfVersionNumbers = 5; - const uint replaceAtVersionNumber = 3; + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + entityStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + projectionStateRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); - TProjection.ShouldRecordAsLatestLogic.Value = (projection, _) => - projection.GetVersionNumber() == new VersionNumber(replaceAtVersionNumber); + await using var entityRepository = await GetWriteEntityRepository(serviceScope, true); + await using var projectionRepository = await GetReadOnlyProjectionRepository(serviceScope, true); - var projectionId = Id.NewId(); - var transaction = TransactionSeeder.Create(projectionId, numberOfVersionNumbers); + const uint numberOfVersions = 5; + const uint replaceAtVersionValue = 3; - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); - projectionSnapshotAdder.AddDependencies.Invoke(serviceCollection); - }); + TProjection.ShouldPersistAsLatestLogic.Value = projection => + projection.GetPointer().StateVersion == new StateVersion(replaceAtVersionValue); - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); + var stateId = Id.NewId(); - await using var projectionRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write, TestSessionOptions.Write); + entityRepository.Create(stateId); - var transactionInserted = await entityRepository.PutTransaction(transaction); + entityRepository.Seed(stateId, replaceAtVersionValue); - // ARRANGE ASSERTIONS + var firstSourceCommitted = await entityRepository.Commit(); - numberOfVersionNumbers.ShouldBeGreaterThan(replaceAtVersionNumber); + entityRepository.Seed(stateId, numberOfVersions - replaceAtVersionValue); - transactionInserted.ShouldBeTrue(); + var secondSourceCommitted = await entityRepository.Commit(); - projectionRepository.SnapshotRepository.ShouldNotBeNull(); + // ARRANGE ASSERTIONS - // ACT + numberOfVersions.ShouldBeGreaterThan(replaceAtVersionValue); - var currentProjection = await projectionRepository.GetSnapshot(projectionId); - var projectionSnapshot = await projectionRepository.SnapshotRepository.GetSnapshotOrDefault(projectionId); + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeTrue(); - // ASSERT + projectionRepository.StateRepository.ShouldNotBeNull(); - currentProjection.GetVersionNumber().Value.ShouldBe(numberOfVersionNumbers); + // ACT + + await projectionRepository.TryLoad(stateId); + + var currentProjection = projectionRepository.Get(stateId); + var persistedProjection = await projectionRepository.StateRepository.Get(stateId); - projectionSnapshot.ShouldNotBe(default); + // ASSERT - projectionSnapshot!.GetVersionNumber().Value.ShouldBe(replaceAtVersionNumber); + currentProjection.GetPointer().StateVersion.Value.ShouldBe(numberOfVersions); + persistedProjection.ShouldNotBeNull(); + persistedProjection.GetPointer().StateVersion.Value.ShouldBe(replaceAtVersionValue); } [Theory] - [MemberData(nameof(AddTransactionsEntitySnapshotsAndProjectionSnapshots))] - public Task GivenEmptyTransactionRepository_WhenGettingProjection_ThenThrow(TransactionsAdder transactionsAdder, - SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + [MemberData(nameof(With_Source_EntityState_ProjectionState))] + public Task GivenEmptySourceRepository_WhenLoadingProjection_ThenReturnFalse(SourceRepositoryAdder sourceRepositoryAdder, + StateRepositoryAdder entityStateRepositoryAdder, StateRepositoryAdder projectionStateRepositoryAdder) { return RunGenericTestAsync ( - new[] { projectionSnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder, projectionSnapshotAdder } + new[] { projectionStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder, projectionStateRepositoryAdder } ); } [Theory] - [MemberData(nameof(AddTransactionsEntitySnapshotsAndProjectionSnapshots))] - public Task GivenTransactionCommitted_WhenGettingProjection_ThenReturnExpectedProjection( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder, SnapshotAdder projectionSnapshotAdder) + [MemberData(nameof(With_Source_EntityState_ProjectionState))] + public Task GivenSourceCommitted_WhenGettingProjection_ThenReturnExpectedProjection( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder, + StateRepositoryAdder projectionStateRepositoryAdder) { return RunGenericTestAsync ( - new[] { entitySnapshotAdder.SnapshotType, projectionSnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, 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 ec1523c6..00000000 --- a/test/EntityDb.Common.Tests/Snapshots/SnapshotTests.cs +++ /dev/null @@ -1,227 +0,0 @@ -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; - -namespace EntityDb.Common.Tests.Snapshots; - -[Collection(nameof(DatabaseContainerCollection))] -public sealed class SnapshotTests : TestsBase -{ - public SnapshotTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) - { - } - - private async Task - Generic_GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInsertedMatchesFetched( - SnapshotAdder snapshotAdder) - where TSnapshot : class, ISnapshotWithTestLogic - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - var snapshotId = Id.NewId(); - var expectedSnapshot = TSnapshot.Construct(snapshotId).WithVersionNumber(new VersionNumber(300)); - - await using var snapshotRepositoryFactory = serviceScope.ServiceProvider - .GetRequiredService>(); - - await using var snapshotRepository = await snapshotRepositoryFactory - .CreateRepository(TestSessionOptions.Write); - - // ACT - - var snapshotInserted = await snapshotRepository.PutSnapshot(snapshotId, expectedSnapshot); - - var actualSnapshot = await snapshotRepository.GetSnapshotOrDefault(snapshotId); - - // ASSERT - - snapshotInserted.ShouldBeTrue(); - - actualSnapshot.ShouldBeEquivalentTo(expectedSnapshot); - } - - [Theory] - [MemberData(nameof(AddEntitySnapshots))] - [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenEmptySnapshotRepository_WhenSnapshotInsertedAndFetched_ThenInsertedMatchesFetched( - SnapshotAdder snapshotAdder) - { - return RunGenericTestAsync - ( - new[] { snapshotAdder.SnapshotType }, - new object?[] { snapshotAdder } - ); - } - - private async Task - Generic_GivenEmptySnapshotRepository_WhenPuttingSnapshotInReadOnlyMode_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).WithVersionNumber(new VersionNumber(300)); - - await using var snapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnly); - - // 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_WhenPuttingSnapshotInReadOnlyMode_ThenCannotWriteInReadOnlyModeExceptionIsLogged( - SnapshotAdder snapshotAdder) - { - return RunGenericTestAsync - ( - new[] { snapshotAdder.SnapshotType }, - new object?[] { snapshotAdder } - ); - } - - 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 - { - // ARRANGE - - var snapshotId = Id.NewId(); - - var expectedSnapshot = TSnapshot.Construct(snapshotId).WithVersionNumber(new VersionNumber(5000)); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - await using var writeSnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.Write); - - await using var readOnlySnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnly); - - await using var readOnlySecondaryPreferredSnapshotRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(TestSessionOptions.ReadOnlySecondaryPreferred); - - var inserted = await writeSnapshotRepository.PutSnapshot(snapshotId, expectedSnapshot); - - // ARRANGE ASSERTIONS - - inserted.ShouldBeTrue(); - - // ACT - - var readOnlySnapshot = await readOnlySnapshotRepository.GetSnapshotOrDefault(snapshotId); - - var readOnlySecondaryPreferredSnapshot = - await readOnlySecondaryPreferredSnapshotRepository.GetSnapshotOrDefault(snapshotId); - - // ASSERT - - readOnlySnapshot.ShouldBeEquivalentTo(expectedSnapshot); - readOnlySecondaryPreferredSnapshot.ShouldBeEquivalentTo(expectedSnapshot); - } - - [Theory] - [MemberData(nameof(AddEntitySnapshots))] - [MemberData(nameof(AddProjectionSnapshots))] - public Task GivenInsertedSnapshot_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 50e7437d..00000000 --- a/test/EntityDb.Common.Tests/Snapshots/TryCatchSnapshotRepositoryTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -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; -using Microsoft.Extensions.Logging; -using Moq; -using Shouldly; -using Xunit; - -namespace EntityDb.Common.Tests.Snapshots; - -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 79% rename from test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs rename to test/EntityDb.Common.Tests/Sources/Agents/AgentAccessorTestsBase.cs index 8cf0215b..69363778 100644 --- a/test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs +++ b/test/EntityDb.Common.Tests/Sources/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; @@ -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,30 +27,33 @@ 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(); // ASSERT - Should.Throw(() => agentAccessor.GetAgentAsync(default!)); + Should.Throw(() => agentAccessor.GetAgent(default!)); } [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); }); @@ -60,7 +63,7 @@ public void GivenBackingServiceActive_WhenGettingAgent_ThenReturnAgent() // ACT - var agent = agentAccessor.GetAgentAsync(default!); + var agent = await agentAccessor.GetAgent(default!); // ASSERT @@ -75,7 +78,7 @@ public async Task GivenBackingServiceActive_ThenCanGetTimestamp() { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); }); @@ -84,7 +87,7 @@ public async Task GivenBackingServiceActive_ThenCanGetTimestamp() var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ASSERT @@ -99,14 +102,14 @@ public async Task GivenBackingServiceActive_WhenGettingAgentSignature_ThenReturn { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); }); var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ACT @@ -126,7 +129,7 @@ public async Task { // ARRANGE - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); @@ -135,7 +138,7 @@ public async Task var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ACT @@ -158,18 +161,15 @@ 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); agentSignatureAugmenterMock - .Setup(x => x.GetApplicationInfoAsync(It.IsAny())) + .Setup(x => x.GetApplicationInfo(It.IsAny())) .ReturnsAsync(expectedApplicationInfo); - using var serviceScope = CreateServiceScope(serviceCollection => + await using var serviceScope = CreateServiceScope(serviceCollection => { ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration); @@ -178,7 +178,7 @@ public async Task var agent = await serviceScope.ServiceProvider .GetRequiredService() - .GetAgentAsync(default!); + .GetAgent(default!); // ACT @@ -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 79% rename from test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs rename to test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs index 7da7c61c..8e48e5c3 100644 --- a/test/EntityDb.Common.Tests/Agents/UnknownAgentAccessorTests.cs +++ b/test/EntityDb.Common.Tests/Sources/Agents/UnknownAgentAccessorTests.cs @@ -1,9 +1,9 @@ -using EntityDb.Common.Agents; +using EntityDb.Common.Sources.Agents; using Microsoft.Extensions.DependencyInjection; -namespace EntityDb.Common.Tests.Agents; +namespace EntityDb.Common.Tests.Sources.Agents; -public class UnknownAgentAccessorTests : AgentAccessorTestsBase +public sealed class UnknownAgentAccessorTests : AgentAccessorTestsBase { public UnknownAgentAccessorTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { @@ -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/EntityStateSourceSubscriberTests.cs b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs new file mode 100644 index 00000000..952de70e --- /dev/null +++ b/test/EntityDb.Common.Tests/Sources/EntityStateSourceSubscriberTests.cs @@ -0,0 +1,119 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Entities; +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 sealed class EntityStateSourceSubscriberTests : TestsBase +{ + public EntityStateSourceSubscriberTests(IServiceProvider startupServiceProvider, + DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) + { + } + + private async Task + Generic_GivenStateShouldPersistAsLatestAlwaysReturnsTrue_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.ShouldPersistAsLatestLogic.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().StateVersion.Value.ShouldBe(numberOfVersions); + } + + [Theory] + [MemberData(nameof(With_Source_EntityState))] + private Task + GivenStateShouldPersistAsLatestAlwaysReturnsTrue_WhenRunningEntityStateSourceSubscriber_ThenAlwaysPersistState( + SourceRepositoryAdder sourceRepositoryAdder, StateRepositoryAdder entityStateRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { entityStateRepositoryAdder.StateType }, + new object?[] { sourceRepositoryAdder, entityStateRepositoryAdder } + ); + } + + private async Task + Generic_GivenStateShouldPersistAsLatestAlwaysReturnsFalse_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.ShouldPersistAsLatestLogic.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 + GivenStateShouldPersistAsLatestAlwaysReturnsFalse_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 new file mode 100644 index 00000000..444839b9 --- /dev/null +++ b/test/EntityDb.Common.Tests/Sources/SourceTests.cs @@ -0,0 +1,1489 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; +using EntityDb.Common.Exceptions; +using EntityDb.Common.Sources.Agents; +using EntityDb.Common.Sources.Queries; +using EntityDb.Common.Sources.Queries.Modified; +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 Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Shouldly; +using System.Diagnostics.CodeAnalysis; +using Xunit; + +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 writeRepository = await GetWriteSourceRepository(serviceScope); + + foreach (var source in sources) + { + var committed = await writeRepository.Commit(source); + + committed.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 + + 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); + 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(); + + // ACT + + var actualTrueResults = + await getActualResults.Invoke(readOnlyRepository, bufferModifier).ToArrayAsync(); + var actualFalseResults = + await getActualResults.Invoke(readOnlyRepository, negateModifier).ToArrayAsync(); + var reversedActualTrueResults = + await getActualResults.Invoke(readOnlyRepository, reverseBufferModifier).ToArrayAsync(); + var reversedActualFalseResults = + await getActualResults.Invoke(readOnlyRepository, reverseNegateModifier).ToArrayAsync(); + var actualSkipTakeResults = + await getActualResults.Invoke(readOnlyRepository, 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, + ISourceDataQuery dataQuery, + 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(dataQuery.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + IMessageDataQuery dataQuery, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateSourceIds(dataQuery.Modify(modifiedQueryOptions)); + } + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseSourceIds + : expectedObjects.TrueSourceIds) + .ToArray(); + } + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + ILeaseDataQuery dataQuery, + 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(dataQuery.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetSourceIds + ( + IServiceScope serviceScope, + ITagDataQuery dataQuery, + 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(dataQuery.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetStateIds + ( + IServiceScope serviceScope, + ISourceDataQuery dataQuery, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) + .Select(statePointer => statePointer.Id); + } + } + + private static async Task TestGetStateIds + ( + IServiceScope serviceScope, + IMessageDataQuery dataQuery, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) + .Select(statePointer => statePointer.Id); + } + } + + private static async Task TestGetStateIds + ( + IServiceScope serviceScope, + ILeaseDataQuery dataQuery, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) + .Select(statePointer => statePointer.Id); + } + } + + private static async Task TestGetStateIds + ( + IServiceScope serviceScope, + ITagDataQuery dataQuery, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + Id[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseStateIds + : expectedObjects.TrueStateIds) + .ToArray(); + } + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository + .EnumerateStatePointers(dataQuery.Modify(modifiedQueryOptions)) + .Select(statePointer => statePointer.Id); + } + } + + private static async Task TestGetAgentSignatures + ( + IServiceScope serviceScope, + ISourceDataQuery dataQuery, + 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(dataQuery.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetDeltas + ( + IServiceScope serviceScope, + IMessageDataQuery dataQuery, + 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(dataQuery.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetLeases + ( + IServiceScope serviceScope, + ILeaseDataQuery dataQuery, + 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(dataQuery.Modify(modifiedQueryOptions)); + } + } + + private static async Task TestGetTags + ( + IServiceScope serviceScope, + ITagDataQuery dataQuery, + ExpectedObjects expectedObjects + ) + { + await TestGet(serviceScope, GetExpectedResults, GetActualResults, true); + await TestGet(serviceScope, GetExpectedResults, GetActualResults, false); + + return; + + IAsyncEnumerable GetActualResults(ISourceRepository sourceRepository, + ModifiedQueryOptions modifiedQueryOptions) + { + return sourceRepository.EnumerateTags(dataQuery.Modify(modifiedQueryOptions)); + } + + ITag[] GetExpectedResults(bool invert) + { + return (invert + ? expectedObjects.FalseTags + : expectedObjects.TrueTags) + .ToArray(); + } + } + + private static Source CreateSource + ( + IEnumerable versionNumbers, + Id? id = null, + TimeStamp? timeStamp = null, + Id? stateId = null, + object? agentSignature = null + ) + { + var nonNullableStateId = stateId ?? Id.NewId(); + + 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(), + StatePointer = nonNullableStateId + new StateVersion(versionNumber), + Delta = new StoreNumber(versionNumber), + }) + .ToArray(), + }; + } + + private static Id[] GetSortedIds(int numberOfIds) + { + return Enumerable + .Range(1, numberOfIds) + .Select(_ => Id.NewId()) + .OrderBy(id => id.Value) + .ToArray(); + } + + private async Task Generic_GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) + where TOptions : class + { + const ulong countTo = 20UL; + const ulong gte = 5UL; + const ulong lte = 15UL; + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + var sourceIds = GetSortedIds((int)countTo); + var stateIds = 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 currentStateId = stateIds[i - 1]; + + var leases = new[] { new CountLease(i) }; + + var tags = new[] { new CountTag(i) }; + + expectedObjects.Add(i is >= gte and <= lte, currentSourceId, currentStateId, agentSignature, deltas, + leases, tags); + + var source = new Source + { + Id = currentSourceId, + TimeStamp = TimeStamp.UtcNow, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta => new Message + { + Id = Id.NewId(), + StatePointer = currentStateId, + Delta = delta, + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), + }) + .ToArray(), + }; + + sources.Add(source); + } + + var options = serviceScope.ServiceProvider + .GetRequiredService>() + .Create("Count"); + + var query = new CountDataQuery(gte, lte, options); + + await PutSources(serviceScope, sources); + 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); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenReadOnlyMode_WhenCommittingSource_ThenReadOnlyWriteExceptionIsLogged( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + var logs = new List(); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.RemoveAll(typeof(ILoggerFactory)); + + serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); + }); + + await using var readOnlyRepository = await GetReadOnlySourceRepository(serviceScope); + + var source = CreateSource(new[] { 1ul }); + + // ACT + + var committed = await readOnlyRepository.Commit(source); + + // ASSERT + + committed.ShouldBeFalse(); + + logs.Count(log => log.Exception is ReadOnlyWriteException).ShouldBe(1); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueSourceIds_WhenCommittingSources_ThenSecondPutReturnsFalse( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var firstSource = CreateSource(new[] { 1ul }); + var secondSource = CreateSource(new[] { 1ul }, firstSource.Id); + + // ACT + + var firstSourceCommitted = await writeRepository.Commit(firstSource); + var secondSourceCommitted = await writeRepository.Commit(secondSource); + + // ASSERT + + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeFalse(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenReturnFalse( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + const int repeatCount = 2; + + var source = CreateSource(new[] { 1ul }); + + source = source with + { + Messages = Enumerable + .Repeat(source.Messages, repeatCount) + .SelectMany(messages => messages) + .ToArray(), + }; + + // ARRANGE ASSERTIONS + + repeatCount.ShouldBeGreaterThan(1); + + // ACT + + var committed = await writeRepository.Commit(source); + + // ASSERT + + committed.ShouldBeFalse(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenVersionZero_WhenCommittingDeltas_ThenReturnTrue(SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var source = CreateSource(new[] { 0ul }); + + // ACT + + var committed = await writeRepository.Commit(source); + + // ASSERT + + committed.ShouldBeTrue(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueVersions_WhenCommittingDeltas_ThenOptimisticConcurrencyExceptionIsLogged( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + var logs = new List(); + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + + serviceCollection.RemoveAll(typeof(ILoggerFactory)); + + serviceCollection.AddSingleton(GetMockedLoggerFactory(logs)); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var stateId = Id.NewId(); + + var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); + var secondSource = CreateSource(new[] { 1ul }, stateId: stateId); + + // ACT + + var firstSourceCommitted = await writeRepository.Commit(firstSource); + var secondSourceCommitted = await writeRepository.Commit(secondSource); + + // ASSERT + + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeFalse(); + + logs.Count(log => log.Exception is OptimisticConcurrencyException).ShouldBe(1); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueTags_WhenCommittingTags_ThenReturnTrue(SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var tag = TagSeeder.Create(); + + var source = new Source + { + Id = default, + TimeStamp = default, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = default, + StatePointer = default, + Delta = new DoNothing(), + AddTags = new[] { tag, tag }.ToArray(), + }, + }, + }; + + // ACT + + var committed = await writeRepository.Commit(source); + + // ASSERT + + committed.ShouldBeTrue(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenNonUniqueLeases_WhenCommittingLeases_ThenReturnFalse( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var lease = LeaseSeeder.Create(); + + var source = new Source + { + Id = default, + TimeStamp = default, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = new[] + { + new Message + { + Id = default, StatePointer = default, Delta = new DoNothing(), AddLeases = new[] { lease, lease }, + }, + }, + }; + + // ACT + + var committed = await writeRepository.Commit(source); + + // ASSERT + + committed.ShouldBeFalse(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenDeltaCommitted_WhenGettingAnnotatedAgentSignature_ThenReturnAnnotatedAgentSignature( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var expectedSourceId = Id.NewId(); + var expectedStateId = Id.NewId(); + var expectedSourceTimeStamp = sourceRepositoryAdder.FixTimeStamp(TimeStamp.UtcNow); + var expectedAgentSignature = new UnknownAgentSignature(new Dictionary()); + + var source = CreateSource + ( + new[] { 1ul }, + expectedSourceId, + expectedSourceTimeStamp, + expectedStateId, + expectedAgentSignature + ); + + var committed = await writeRepository.Commit(source); + + var query = new StateDataQuery(expectedStateId); + + // ARRANGE ASSERTIONS + + committed.ShouldBeTrue(); + + // ACT + + var annotatedAgentSignatures = await writeRepository + .EnumerateAnnotatedAgentSignatures(query) + .ToArrayAsync(); + + // ASSERT + + annotatedAgentSignatures.Length.ShouldBe(1); + + annotatedAgentSignatures[0].SourceId.ShouldBe(expectedSourceId); + annotatedAgentSignatures[0].SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); + annotatedAgentSignatures[0].StatePointers.Length.ShouldBe(1); + annotatedAgentSignatures[0].StatePointers[0].Id.ShouldBe(expectedStateId); + annotatedAgentSignatures[0].Data.ShouldBeEquivalentTo(expectedAgentSignature); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenDeltaCommitted_WhenGettingAnnotatedDeltas_ThenReturnAnnotatedDelta( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + ulong[] numbers = { 1, 2, 3, 4, 5 }; + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var expectedSourceId = Id.NewId(); + var expectedStateId = Id.NewId(); + var expectedSourceTimeStamp = sourceRepositoryAdder.FixTimeStamp(TimeStamp.UtcNow); + + var source = CreateSource + ( + numbers, + expectedSourceId, + expectedSourceTimeStamp, + expectedStateId + ); + + var committed = await writeRepository.Commit(source); + + var query = new GetDeltasDataQuery(expectedStateId, default); + + // ARRANGE ASSERTIONS + + committed.ShouldBeTrue(); + + // ACT + + var annotatedDeltas = await writeRepository.EnumerateAnnotatedDeltas(query).ToArrayAsync(); + + // ASSERT + + annotatedDeltas.Length.ShouldBe(numbers.Length); + + foreach (var number in numbers) + { + var annotatedDelta = annotatedDeltas[Convert.ToInt32(number) - 1]; + + annotatedDelta.SourceId.ShouldBe(expectedSourceId); + annotatedDelta.SourceTimeStamp.ShouldBe(expectedSourceTimeStamp); + annotatedDelta.StatePointer.Id.ShouldBe(expectedStateId); + annotatedDelta.StatePointer.StateVersion.ShouldBe(new StateVersion(number)); + + annotatedDelta.Data + .ShouldBeAssignableTo() + .ShouldNotBeNull() + .Number + .ShouldBe(number); + } + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenMessageWithTagsCommitted_WhenRemovingAllTags_ThenSourceHasNoTags( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var stateId = Id.NewId(); + + var tag = TagSeeder.Create(); + var tags = new[] { tag }; + + 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(), StatePointer = stateId, AddTags = tags, + }, + }, + }; + + var initialSourceCommitted = await writeRepository.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(), StatePointer = stateId, DeleteTags = tags, + }, + }, + }; + + var deleteTagsQuery = new DeleteTagsDataQuery(stateId, tags); + + // ARRANGE ASSERTIONS + + initialSourceCommitted.ShouldBeTrue(); + + // ACT + + var initialTags = await writeRepository + .EnumerateTags(deleteTagsQuery) + .ToArrayAsync(); + + var finalSourceCommitted = await writeRepository.Commit(finalSource); + + var finalTags = await writeRepository + .EnumerateTags(deleteTagsQuery) + .ToArrayAsync(); + + // ASSERT + + initialTags.Length.ShouldBe(1); + initialTags[0].ShouldBeEquivalentTo(tag); + finalSourceCommitted.ShouldBeTrue(); + finalTags.ShouldBeEmpty(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenMessageWithLeasesCommitted_WhenRemovingAllLeases_ThenSourceHasNoLeases( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var lease = LeaseSeeder.Create(); + var leases = new[] { lease }; + + 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(), StatePointer = default, AddLeases = leases, + }, + }, + }; + + var initialSourceCommitted = await writeRepository.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(), StatePointer = default, DeleteLeases = leases, + }, + }, + }; + + var leaseQuery = new DeleteLeasesDataQuery(leases); + + // ARRANGE ASSERTIONS + + initialSourceCommitted.ShouldBeTrue(); + + // ACT + + var initialLeases = await writeRepository + .EnumerateLeases(leaseQuery) + .ToArrayAsync(); + + var finalSourceCommitted = await writeRepository.Commit(finalSource); + + var finalLeases = await writeRepository + .EnumerateLeases(leaseQuery) + .ToArrayAsync(); + + // ASSERT + + initialLeases.Length.ShouldBe(1); + finalSourceCommitted.ShouldBeTrue(); + finalLeases.ShouldBeEmpty(); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenMessageCommitted_WhenQueryingForVersionOne_ThenReturnTheExpectedDelta( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var expectedDelta = new StoreNumber(1); + + var source = CreateSource(new[] { 1ul }); + + var versionOneQuery = new StateVersionDataQuery(new StateVersion(1), new StateVersion(1)); + + // ACT + + var committed = await writeRepository.Commit(source); + + var newDeltas = await writeRepository.EnumerateDeltas(versionOneQuery).ToArrayAsync(); + + // ASSERT + + committed.ShouldBeTrue(); + newDeltas.Length.ShouldBe(1); + newDeltas[0].ShouldBeEquivalentTo(expectedDelta); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenTwoMessagesCommitted_WhenQueryingForVersionTwo_ThenReturnExpectedDelta( + SourceRepositoryAdder sourceRepositoryAdder) + { + // ARRANGE + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + await using var writeRepository = await GetWriteSourceRepository(serviceScope); + + var expectedDelta = new StoreNumber(2); + + var stateId = Id.NewId(); + var firstSource = CreateSource(new[] { 1ul }, stateId: stateId); + var secondSource = CreateSource(new[] { 2ul }, stateId: stateId); + + var versionTwoQuery = new StateVersionDataQuery(new StateVersion(2), new StateVersion(2)); + + // ACT + + var firstSourceCommitted = await writeRepository.Commit(firstSource); + + var secondSourceCommitted = await writeRepository.Commit(secondSource); + + var newDeltas = await writeRepository.EnumerateDeltas(versionTwoQuery).ToArrayAsync(); + + // ASSERT + + firstSourceCommitted.ShouldBeTrue(); + secondSourceCommitted.ShouldBeTrue(); + newDeltas.Length.ShouldBe(1); + newDeltas[0].ShouldBeEquivalentTo(expectedDelta); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceTimeStamp_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) + { + const ulong timeSpanInMinutes = 60UL; + const ulong gteInMinutes = 20UL; + const ulong lteInMinutes = 30UL; + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + var originTimeStamp = TimeStamp.UnixEpoch; + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + var sourceIds = GetSortedIds((int)timeSpanInMinutes); + var stateIds = GetSortedIds((int)timeSpanInMinutes); + + TimeStamp? gte = null; + TimeStamp? lte = null; + + for (var i = 1UL; i <= timeSpanInMinutes; i++) + { + var currentSourceId = sourceIds[i - 1]; + var currentStateId = stateIds[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, currentStateId, + agentSignature, deltas, leases, tags); + + switch (i) + { + case lteInMinutes: + lte = currentSourceTimeStamp; + break; + + case gteInMinutes: + gte = currentSourceTimeStamp; + break; + } + + var source = new Source + { + Id = currentSourceId, + TimeStamp = currentSourceTimeStamp, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta => new Message + { + Id = Id.NewId(), + StatePointer = currentStateId, + Delta = delta, + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), + }) + .ToArray(), + }; + + sources.Add(source); + } + + gte.ShouldNotBeNull(); + lte.ShouldNotBeNull(); + + var query = new SourceTimeStampDataQuery(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 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); + await TestGetTags(serviceScope, query, expectedObjects); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingBySourceId_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) + { + const ulong numberOfSourceIds = 10UL; + const ulong whichSourceId = 5UL; + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + Id? sourceId = null; + + var sourceIds = 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 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, currentStateId, agentSignature, + deltas, + leases, tags); + + if (i == whichSourceId) + { + sourceId = currentSourceId; + } + + var source = new Source + { + Id = currentSourceId, + TimeStamp = TimeStamp.UtcNow, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta => new Message + { + Id = Id.NewId(), + StatePointer = currentStateId, + Delta = delta, + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), + }) + .ToArray(), + }; + + sources.Add(source); + } + + sourceId.ShouldNotBeNull(); + + var query = new SourceIdDataQuery(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 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); + await TestGetTags(serviceScope, query, expectedObjects); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateId_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) + { + const ulong numberOfStateIds = 10UL; + const ulong whichStateId = 5UL; + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.AddDependencies.Invoke(serviceCollection); + }); + + var sources = new List(); + var expectedObjects = new ExpectedObjects(); + + Id? stateId = null; + + var sourceIds = GetSortedIds((int)numberOfStateIds); + var stateIds = GetSortedIds((int)numberOfStateIds); + + var agentSignature = new UnknownAgentSignature(new Dictionary()); + + for (var i = 1UL; i <= numberOfStateIds; i++) + { + var currentSourceId = sourceIds[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 == whichStateId, currentSourceId, currentStateId, agentSignature, deltas, + leases, tags); + + if (i == whichStateId) + { + stateId = currentStateId; + } + + var source = new Source + { + Id = currentSourceId, + TimeStamp = TimeStamp.UtcNow, + AgentSignature = agentSignature, + Messages = deltas + .Select(delta => new Message + { + Id = Id.NewId(), + StatePointer = currentStateId, + Delta = delta, + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), + }) + .ToArray(), + }; + + sources.Add(source); + } + + stateId.ShouldNotBeNull(); + + var query = new StateDataQuery(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 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); + await TestGetTags(serviceScope, query, expectedObjects); + } + + [Theory] + [MemberData(nameof(With_Source))] + public async Task GivenSourceAlreadyCommitted_WhenQueryingByStateVersion_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) + { + const ulong numberOfVersions = 20; + const ulong gte = 5UL; + const ulong lte = 15UL; + + await using var serviceScope = CreateServiceScope(serviceCollection => + { + sourceRepositoryAdder.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(), + StatePointer = default, + Delta = delta, + AddLeases = leases.ToArray(), + AddTags = tags.ToArray(), + }); + } + + var source = new Source + { + Id = Id.NewId(), + TimeStamp = TimeStamp.UtcNow, + AgentSignature = new UnknownAgentSignature(new Dictionary()), + Messages = messages.ToArray(), + }; + + var sources = new List { source }; + + var query = new StateVersionDataQuery(new StateVersion(gte), new StateVersion(lte)); + + await PutSources(serviceScope, sources); + await TestGetDeltas(serviceScope, query, expectedObjects); + await TestGetLeases(serviceScope, query, expectedObjects); + await TestGetTags(serviceScope, query, expectedObjects); + } + + [Theory] + [MemberData(nameof(With_Source))] + public Task GivenSourceAlreadyCommitted_WhenQueryingByData_ThenReturnExpectedObjects( + SourceRepositoryAdder sourceRepositoryAdder) + { + return RunGenericTestAsync + ( + new[] { sourceRepositoryAdder.QueryOptionsType }, + new object?[] { sourceRepositoryAdder } + ); + } + + private sealed class ExpectedObjects + { + public readonly List FalseAgentSignatures = new(); + public readonly List FalseDeltas = 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 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 stateId, + object agentSignature, + IEnumerable deltas, + IEnumerable leases, + IEnumerable tags + ) + { + if (condition) + { + TrueSourceIds.Add(sourceId); + TrueStateIds.Add(stateId); + TrueAgentSignatures.Add(agentSignature); + TrueDeltas.AddRange(deltas); + TrueLeases.AddRange(leases); + TrueTags.AddRange(tags); + } + else + { + FalseSourceIds.Add(sourceId); + FalseStateIds.Add(stateId); + FalseAgentSignatures.Add(agentSignature); + FalseDeltas.AddRange(deltas); + FalseLeases.AddRange(leases); + FalseTags.AddRange(tags); + } + } + } +} diff --git a/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs new file mode 100644 index 00000000..211da4a8 --- /dev/null +++ b/test/EntityDb.Common.Tests/Sources/TryCatchSourceRepositoryTests.cs @@ -0,0 +1,155 @@ +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 sealed class TryCatchSourceRepositoryTests : TestsBase +{ + public TryCatchSourceRepositoryTests(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + [Fact] + public async Task GivenRepositoryAlwaysThrows_WhenExecutingAnyMethod_ThenExceptionIsLogged() + { + // ARRANGE + + var logs = new List(); + + var loggerFactory = GetMockedLoggerFactory(logs); + + 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.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateStatePointers(It.IsAny(), It.IsAny())) + .Throws(new NotImplementedException()); + + sourceRepositoryMock + .Setup(repository => + repository.EnumerateStatePointers(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 sourceIdsFromSourceDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ISourceDataQuery)!).ToArrayAsync(); + var sourceIdsFromMessageDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(IMessageDataQuery)!).ToArrayAsync(); + var sourceIdsFromLeaseDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ILeaseDataQuery)!).ToArrayAsync(); + var sourceIdsFromTagDataQuery = + await tryCatchSourceRepository.EnumerateSourceIds(default(ITagDataQuery)!).ToArrayAsync(); + var statePointersFromSourceDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ISourceDataQuery)!).ToArrayAsync(); + var statePointersFromMessageDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(IMessageDataQuery)!).ToArrayAsync(); + var statePointersFromLeaseDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ILeaseDataQuery)!).ToArrayAsync(); + var statePointersFromTagDataQuery = + await tryCatchSourceRepository.EnumerateStatePointers(default(ITagDataQuery)!).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 committed = + await tryCatchSourceRepository.Commit(default!); + + // ASSERT + + sourceIdsFromSourceDataQuery.ShouldBeEmpty(); + sourceIdsFromMessageDataQuery.ShouldBeEmpty(); + sourceIdsFromLeaseDataQuery.ShouldBeEmpty(); + sourceIdsFromTagDataQuery.ShouldBeEmpty(); + statePointersFromSourceDataQuery.ShouldBeEmpty(); + statePointersFromMessageDataQuery.ShouldBeEmpty(); + statePointersFromLeaseDataQuery.ShouldBeEmpty(); + statePointersFromTagDataQuery.ShouldBeEmpty(); + agentSignatures.ShouldBeEmpty(); + deltas.ShouldBeEmpty(); + leases.ShouldBeEmpty(); + tags.ShouldBeEmpty(); + annotatedDeltas.ShouldBeEmpty(); + committed.ShouldBeFalse(); + + logs.Count(log => log.Exception is NotImplementedException).ShouldBe(14); + } +} diff --git a/test/EntityDb.Common.Tests/Startup.cs b/test/EntityDb.Common.Tests/Startup.cs index 52e64f1f..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 { -} \ No newline at end of file +} diff --git a/test/EntityDb.Common.Tests/StartupBase.cs b/test/EntityDb.Common.Tests/StartupBase.cs index 86c466b3..fe728df9 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; @@ -19,5 +19,9 @@ public virtual void AddServices(IServiceCollection serviceCollection) // Agent Accessor serviceCollection.AddAgentAccessor(); + + // Source Processor Queue + + 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..42ae991f --- /dev/null +++ b/test/EntityDb.Common.Tests/States/StateTests.cs @@ -0,0 +1,215 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.States; +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; + +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 StateVersion(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 StateVersion(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.ShouldPersistAsLatestLogic.Value = _ => true; + + StatePointer latestPointer = Id.NewId(); + + var stateSnapshot = TState.Construct(latestPointer.Id + new StateVersion(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 StateVersion(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..a213d734 --- /dev/null +++ b/test/EntityDb.Common.Tests/States/TryCatchStateRepositoryTests.cs @@ -0,0 +1,84 @@ +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.States; +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 sealed 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..20623d92 --- /dev/null +++ b/test/EntityDb.Common.Tests/Streams/StreamTests.cs @@ -0,0 +1,342 @@ +using EntityDb.Abstractions; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Attributes; +using EntityDb.Abstractions.Sources.Queries; +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.Tests.Implementations.Entities.Deltas; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Shouldly; +using Xunit; + +namespace EntityDb.Common.Tests.Streams; + +[Collection(nameof(DatabaseContainerCollection))] +public sealed 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); + + IStateKey streamKey = new Key("StreamKey"); + var expectedStreamKeyLease = streamKey.ToLease(); + + IMessageKey messageKey = new Key("MessageKey"); + var expectedMessageKeyLease = messageKey.ToLease(streamKey); + + var expectedDelta = new DoNothingIdempotent(messageKey); + + // ACT + + await writeRepository.LoadOrCreate(streamKey); + + var staged = await writeRepository + .Append(streamKey, 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.StateVersion.ShouldBe(StateVersion.One); + committedSources[0].Messages[0].Delta.ShouldBe(expectedDelta); + 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] + [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); + + IStateKey streamKey = new Key("StreamKey"); + var streamKeyLease = streamKey.ToLease(); + + IMessageKey messageKey = new Key("MessageKey"); + var messageKeyLease = messageKey.ToLease(streamKey); + + // ACT + + await writeRepository.LoadOrCreate(streamKey); + + var staged = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey)); + + var committed = await writeRepository.Commit(); + + var statePointerCount = await writeRepository.SourceRepository + .EnumerateStatePointers(new MatchingLeasesDataQuery(streamKeyLease, messageKeyLease)) + .CountAsync(); + + // ASSERT + + staged.ShouldBeTrue(); + committed.ShouldBeTrue(); + statePointerCount.ShouldBe(1); + } + + [Fact] + public async Task GivenExistingStreamMock_WhenStagingNewMessageKey_ThenCommittedSourceIsCorrect() + { + // ARRANGE + + var statePointer = Id.NewId() + StateVersion.One; + + 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); + + IStateKey streamKey = new Key("StreamKey"); + IMessageKey messageKey = new Key("MessageKey"); + + var expectedLease = messageKey.ToLease(streamKey); + + // ARRANGE ASSERTIONS + + statePointer.StateVersion.ShouldNotBe(StateVersion.Zero); + + // ACT + + await writeRepository.LoadOrCreate(streamKey); + + var staged = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey)); + + 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.StateVersion.ShouldBe(StateVersion.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); + + IStateKey streamKey = new Key("StreamKey"); + var streamKeyLease = streamKey.ToLease(); + + IMessageKey messageKey1 = new Key("MessageKey1"); + var messageKeyLease1 = messageKey1.ToLease(streamKey); + + IMessageKey messageKey2 = new Key("MessageKey2"); + var messageKeyLease2 = messageKey2.ToLease(streamKey); + + await writeRepository.LoadOrCreate(streamKey); + + var firstStaged = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey1)); + + var firstCommitted = await writeRepository.Commit(); + + // ARRANGE ASSERTIONS + + firstStaged.ShouldBeTrue(); + firstCommitted.ShouldBeTrue(); + + // ACT + + var secondStaged = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey2)); + + var secondCommitted = await writeRepository.Commit(); + + var statePointerCount = await writeRepository.SourceRepository + .EnumerateStatePointers(new MatchingLeasesDataQuery(streamKeyLease, messageKeyLease1, messageKeyLease2)) + .CountAsync(); + + // ASSERT + + secondStaged.ShouldBeTrue(); + secondCommitted.ShouldBeTrue(); + statePointerCount.ShouldBe(2); + } + + [Fact] + public async Task GivenExistingStreamMock_WhenStagingDuplicateMessageKey_ThenStageReturnsFalse() + { + // ARRANGE + + var statePointer = Id.NewId() + StateVersion.One; + + 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); + + IStateKey streamKey = new Key("StreamKey"); + IMessageKey messageKey = new Key("MessageKey"); + + await writeRepository.LoadOrCreate(streamKey); + + // ACT + + var staged = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey)); + + // 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); + + IStateKey streamKey = new Key("StreamKey"); + IMessageKey messageKey = new Key("MessageKey"); + + await writeRepository.LoadOrCreate(streamKey); + + var stagedOnce = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey)); + + var committedOnce = await writeRepository.Commit(); + + // ARRANGE ASSERTIONS + + stagedOnce.ShouldBeTrue(); + committedOnce.ShouldBeTrue(); + + // ACT + + var stagedTwice = await writeRepository + .Append(streamKey, new DoNothingIdempotent(messageKey)); + + // ASSERT + + stagedTwice.ShouldBeFalse(); + } +} diff --git a/test/EntityDb.Common.Tests/TestLogger.cs b/test/EntityDb.Common.Tests/TestLogger.cs index b2404156..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; @@ -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 f8bdd058..a9fa2ae4 100644 --- a/test/EntityDb.Common.Tests/TestSessionOptions.cs +++ b/test/EntityDb.Common.Tests/TestSessionOptions.cs @@ -2,7 +2,9 @@ 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 ef397161..2778f81e 100644 --- a/test/EntityDb.Common.Tests/TestsBase.cs +++ b/test/EntityDb.Common.Tests/TestsBase.cs @@ -1,136 +1,89 @@ -using System.Diagnostics; -using System.Reflection; -using EntityDb.Abstractions.Queries; -using EntityDb.Abstractions.Snapshots; -using EntityDb.Abstractions.Transactions; -using EntityDb.Abstractions.ValueObjects; -using EntityDb.Common.Entities; +using EntityDb.Abstractions.Entities; +using EntityDb.Abstractions.Projections; +using EntityDb.Abstractions.Sources; +using EntityDb.Abstractions.Sources.Queries; +using EntityDb.Abstractions.States; +using EntityDb.Abstractions.Streams; +using EntityDb.Common.Disposables; 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.InMemory.Extensions; -using EntityDb.InMemory.Sessions; +using EntityDb.Common.Tests.Implementations.States; using EntityDb.MongoDb.Extensions; -using EntityDb.MongoDb.Queries; -using EntityDb.MongoDb.Sessions; -using EntityDb.Npgsql.Extensions; -using EntityDb.Npgsql.Queries; +using EntityDb.MongoDb.Sources.Queries; +using EntityDb.MongoDb.Sources.Sessions; +using EntityDb.MongoDb.States.Sessions; using EntityDb.Redis.Extensions; -using EntityDb.Redis.Sessions; -using EntityDb.SqlDb.Sessions; -using Microsoft.EntityFrameworkCore; +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; using Xunit.Sdk; -using Pointer = EntityDb.Abstractions.ValueObjects.Pointer; namespace EntityDb.Common.Tests; -public class TestsBase +public abstract class TestsBase where TStartup : IStartup, new() { public delegate void AddDependenciesDelegate(IServiceCollection serviceCollection); - private static readonly TransactionsAdder[] AllTransactionAdders = + private static readonly SourceRepositoryAdder[] AllSourceRepositoryAdders = { - 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.Configure("Count", options => - { - options.FindOptions = new FindOptions - { - Collation = new Collation("en", numericOrdering: true) - }; - }); - - serviceCollection.ConfigureAll(options => + new("MongoDb", typeof(MongoDbQueryOptions), timeStamp => timeStamp.WithMillisecondPrecision(), + serviceCollection => { - options.ConnectionString = databaseContainerFixture!.MongoDbContainer.ConnectionString.Replace("mongodb://:@", "mongodb://"); - options.DatabaseName = databaseContainerFixture.MongoDbConfiguration.Database; - options.WriteTimeout = TimeSpan.FromSeconds(1); - }); - - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + var databaseContainerFixture = (serviceCollection + .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) + .ImplementationInstance as DatabaseContainerFixture)!; - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => - { - options.ReadOnly = true; - options.SecondaryPreferred = false; - }); + serviceCollection.AddMongoDbSources(true, true); - 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.ConnectionString};Include Error Detail=true"; - }); + serviceCollection.Configure("Count", options => + { + options.FindOptions = new FindOptions { Collation = new Collation("en", numericOrdering: true) }; + }); - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + serviceCollection.ConfigureAll(options => + { + options.ConnectionString = databaseContainerFixture.MongoDbContainer.GetConnectionString(); + 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; - }); - }) + 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(); @@ -154,200 +107,207 @@ protected Task RunGenericTestAsync(Type[] typeArguments, object?[] invokeParamet .ShouldNotBeNull(); } - private static SnapshotAdder EntityFrameworkSnapshotAdder() - where TSnapshot : class, ISnapshotWithTestLogic + private static StateRepositoryAdder MongoDbStateRepositoryAdder() + where TState : class, IStateWithTestLogic { - return new SnapshotAdder($"EntityFramework<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => + return new StateRepositoryAdder($"MongoDb<{typeof(TState).Name}>", typeof(TState), serviceCollection => { - var databaseContainerFixture = serviceCollection + var databaseContainerFixture = (serviceCollection .Single(descriptor => descriptor.ServiceType == typeof(DatabaseContainerFixture)) - .ImplementationInstance as DatabaseContainerFixture; + .ImplementationInstance as DatabaseContainerFixture)!; - serviceCollection.AddEntityFrameworkSnapshots>(testMode: true); + serviceCollection.AddMongoDbStateRepository(true, true); - serviceCollection.ConfigureAll(options => + serviceCollection.ConfigureAll(options => { - options.ConnectionString = databaseContainerFixture!.PostgreSqlContainer.ConnectionString; + options.ConnectionString = databaseContainerFixture.MongoDbContainer.GetConnectionString(); + options.DatabaseName = DatabaseContainerFixture.OmniParameter; + options.CollectionName = TState.MongoDbCollectionName; + 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 => + serviceCollection.Configure(TestSessionOptions.ReadOnly, options => { options.ReadOnly = true; + options.SecondaryPreferred = false; }); - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - }); + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + options => + { + options.ReadOnly = true; + options.SecondaryPreferred = true; + }); }); } - 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.ConnectionString; - options.KeyNamespace = TSnapshot.RedisKeyNamespace; + options.ConnectionString = databaseContainerFixture!.RedisContainer.GetConnectionString(); + options.KeyNamespace = TState.RedisKeyNamespace; }); - serviceCollection.Configure>(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + 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 => - { - options.ReadOnly = true; - options.SecondaryPreferred = true; - }); + serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, + options => + { + options.ReadOnly = true; + options.SecondaryPreferred = true; + }); }); } - private static SnapshotAdder InMemorySnapshotAdder() - where TSnapshot : class, ISnapshotWithTestLogic + private static IEnumerable AllStateRepositoryAdders() + where TState : class, IStateWithTestLogic { - return new SnapshotAdder($"InMemory<{typeof(TSnapshot).Name}>", typeof(TSnapshot), serviceCollection => - { - serviceCollection.AddInMemorySnapshots(true); - - serviceCollection.Configure(TestSessionOptions.Write, options => - { - options.ReadOnly = false; - }); + yield return RedisStateRepositoryAdder(); + yield return MongoDbStateRepositoryAdder(); + } - serviceCollection.Configure(TestSessionOptions.ReadOnly, options => - { - options.ReadOnly = true; - }); + private static EntityRepositoryAdder GetEntityRepositoryAdder() + where TEntity : IEntity + { + return new EntityRepositoryAdder(typeof(TEntity).Name, typeof(TEntity), + serviceCollection => { serviceCollection.AddEntityRepository(); }); + } - serviceCollection.Configure(TestSessionOptions.ReadOnlySecondaryPreferred, options => - { - options.ReadOnly = true; - }); - }); + private static IEnumerable AllEntityRepositoryAdders() + { + yield return GetEntityRepositoryAdder(); } - private static IEnumerable AllSnapshotAdders() - where TSnapshot : class, ISnapshotWithTestLogic + private static ProjectionRepositoryAdder GetProjectionRepositoryAdder() + where TProjection : IProjection { - yield return EntityFrameworkSnapshotAdder(); - yield return RedisSnapshotAdder(); - yield return InMemorySnapshotAdder(); + return new ProjectionRepositoryAdder(typeof(TProjection).Name, typeof(TProjection), serviceCollection => + { + serviceCollection.AddProjectionRepository(); + }); } - private static EntityAdder GetEntityAdder() - where TEntity : IEntity + private static IEnumerable AllProjectionRepositoryAdders() { - return new EntityAdder(typeof(TEntity).Name, typeof(TEntity), - serviceCollection => { serviceCollection.AddEntity(); }); + yield return GetProjectionRepositoryAdder(); } - private static IEnumerable AllEntitySnapshotAdders() - where TEntity : class, IEntity, ISnapshotWithTestLogic + private static IEnumerable GetEntityStateRepositoryAdders() + where TEntity : class, IEntity, IStateWithTestLogic { return - from snapshotAdder in AllSnapshotAdders() - let entityAdder = GetEntityAdder() - select new SnapshotAdder(snapshotAdder.Name, snapshotAdder.SnapshotType, - snapshotAdder.AddDependencies + entityAdder.AddDependencies + (serviceCollection => + from stateRepositoryAdder in AllStateRepositoryAdders() + let entityRepositoryAdder = GetEntityRepositoryAdder() + select stateRepositoryAdder with { - serviceCollection.AddEntitySnapshotTransactionSubscriber(TestSessionOptions.ReadOnly, - TestSessionOptions.Write, true); - })); + AddDependencies = stateRepositoryAdder.AddDependencies + entityRepositoryAdder.AddDependencies + + (serviceCollection => + { + serviceCollection.AddEntityStateSourceSubscriber( + TestSessionOptions.ReadOnly, + TestSessionOptions.Write); + }), + }; } - private static IEnumerable AllEntityAdders() + private static IEnumerable GetProjectionStateRepositoryAdders() + where TProjection : class, IProjection, IStateWithTestLogic { - yield return GetEntityAdder(); + return + from stateRepositoryAdder in AllStateRepositoryAdders() + let projectionRepositoryAdder = GetProjectionRepositoryAdder() + select stateRepositoryAdder with + { + AddDependencies = stateRepositoryAdder.AddDependencies + projectionRepositoryAdder.AddDependencies + + (serviceCollection => + { + serviceCollection.AddProjectionStateSourceSubscriber( + TestSessionOptions.Write); + }), + }; } - private static IEnumerable AllEntitySnapshotAdders() + private static IEnumerable AllEntityStateRepositoryAdders() { - return Enumerable.Empty() - .Concat(AllEntitySnapshotAdders()); + return Enumerable.Empty() + .Concat(GetEntityStateRepositoryAdders()); } - private static IEnumerable AllProjectionAdders() - where TProjection : class, IProjection, ISnapshotWithTestLogic + private static IEnumerable AllProjectionStateRepositoryAdders() { - return AllSnapshotAdders() - .Select(snapshotAdder => new SnapshotAdder(snapshotAdder.Name, snapshotAdder.SnapshotType, - snapshotAdder.AddDependencies + (serviceCollection => - { - serviceCollection.AddProjection(); - serviceCollection.AddProjectionSnapshotTransactionSubscriber( - TestSessionOptions.ReadOnly, TestSessionOptions.Write, true); - })) - ); + return Enumerable.Empty() + .Concat(GetProjectionStateRepositoryAdders()); } - private static IEnumerable AllProjectionSnapshotAdders() + public static IEnumerable With_Source_Entity() { - return Enumerable.Empty() - .Concat(AllProjectionAdders()); + return + from sourceRepositoryAdder in AllSourceRepositoryAdders + from entityRepositoryAdder in AllEntityRepositoryAdders() + select new object[] { sourceRepositoryAdder, entityRepositoryAdder }; } - public static IEnumerable AddTransactionsAndEntity() + public static IEnumerable With_Source() { - return from transactionAdder in AllTransactionAdders - from entityAdder in AllEntityAdders() - select new object[] { transactionAdder, entityAdder }; + return + from sourceRepositoryAdder in AllSourceRepositoryAdders + select new object[] { sourceRepositoryAdder }; } - public static IEnumerable AddEntity() + public static IEnumerable With_Entity() { - return from entityAdder in AllEntityAdders() - select new object[] { entityAdder }; + return from entityRepositoryAdder in AllEntityRepositoryAdders() + select new object[] { entityRepositoryAdder }; } - public static IEnumerable AddEntitySnapshots() + public static IEnumerable With_EntityState() { - return from entitySnapshotAdder in AllEntitySnapshotAdders() - select new object[] { entitySnapshotAdder }; + return from entityStateRepositoryAdder in AllEntityStateRepositoryAdders() + select new object[] { entityStateRepositoryAdder }; } - public static IEnumerable AddProjectionSnapshots() + public static IEnumerable With_ProjectionState() { - return from projectionSnapshotAdder in AllProjectionSnapshotAdders() - select new object[] { projectionSnapshotAdder }; + return from projectionStateRepositoryAdder in AllProjectionStateRepositoryAdders() + select new object[] { projectionStateRepositoryAdder }; } - public static IEnumerable AddTransactionsAndEntitySnapshots() + public static IEnumerable With_Source_EntityState() { - return from transactionAdder in AllTransactionAdders - from entitySnapshotAdder in AllEntitySnapshotAdders() - select new object[] { transactionAdder, entitySnapshotAdder }; + return from sourceRepositoryAdder in AllSourceRepositoryAdders + from entityStateRepositoryAdder in AllEntityStateRepositoryAdders() + select new object[] { sourceRepositoryAdder, entityStateRepositoryAdder }; } - public static IEnumerable AddTransactionsEntitySnapshotsAndProjectionSnapshots() + public static IEnumerable With_Source_EntityState_ProjectionState() { - return from transactionAdder in AllTransactionAdders - from entitySnapshotAdder in AllEntitySnapshotAdders() - from projectionSnapshotAdder in AllProjectionSnapshotAdders() - select new object[] { transactionAdder, entitySnapshotAdder, projectionSnapshotAdder }; + return from sourceRepositoryAdder in AllSourceRepositoryAdders + from entityStateRepositoryAdder in AllEntityStateRepositoryAdders() + from projectionStateRepositoryAdder in AllProjectionStateRepositoryAdders() + select new object[] { sourceRepositoryAdder, entityStateRepositoryAdder, projectionStateRepositoryAdder }; } - protected IServiceScope CreateServiceScope(Action? configureServices = null) + internal TestServiceScope CreateServiceScope(Action? configureServices = null) { var serviceCollection = new ServiceCollection(); @@ -355,18 +315,16 @@ 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; }); }); - serviceCollection.Configure(x => - { - x.MinLevel = LogLevel.Debug; - }); + serviceCollection.Configure(x => { x.MinLevel = LogLevel.Debug; }); startup.AddServices(serviceCollection); @@ -383,150 +341,295 @@ protected IServiceScope CreateServiceScope(Action? configure var serviceScopeFactory = singletonServiceProvider.GetRequiredService(); - return new TestServiceScope(singletonServiceProvider, serviceScopeFactory.CreateScope()); + return new TestServiceScope + { + SingletonServiceProvider = singletonServiceProvider, + AsyncServiceScope = serviceScopeFactory.CreateAsyncScope(), + }; + } + + 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 + protected static ISourceSubscriber GetMockedSourceSubscriber(List committedSources) { - var disposableMock = new Mock(MockBehavior.Strict); + var sourceSubscriberMock = new Mock(); - disposableMock.Setup(disposable => disposable.Dispose()); + sourceSubscriberMock + .Setup(subscriber => subscriber.Notify(It.IsAny())) + .Callback((Source source) => + { + committedSources.Add(source); + }); - var loggerMock = new Mock(MockBehavior.Strict); + return sourceSubscriberMock.Object; + } - loggerMock - .Setup(logger => logger.BeginScope(It.IsAny())) - .Returns(disposableMock.Object); + protected static Task> GetWriteEntityRepository( + IServiceScope serviceScope, bool getPersistedState) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateMultiple + ( + TestSessionOptions.Default, + TestSessionOptions.Write, + getPersistedState ? TestSessionOptions.Write : null + ); + } - loggerMock - .Setup(logger => logger.Log + protected static Task> GetReadOnlyEntityRepository( + IServiceScope serviceScope, bool getPersistedState) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateMultiple ( - 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 + TestSessionOptions.Default, + TestSessionOptions.ReadOnly, + getPersistedState ? TestSessionOptions.ReadOnly : null + ); + } + + protected static Task GetWriteStreamRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .CreateMultiple ( - It.Is(logLevel => logLevel == LogLevel.Error), - It.IsAny(), - It.IsAny(), - It.Is(exception => exception is TException), - It.IsAny>() - )) - .Verifiable(); + TestSessionOptions.Default, + TestSessionOptions.Write + ); + } - var loggerFactoryMock = new Mock(MockBehavior.Strict); + protected static Task GetReadOnlyStreamRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .CreateMultiple + ( + TestSessionOptions.Default, + TestSessionOptions.ReadOnly + ); + } - loggerFactoryMock - .Setup(factory => factory.CreateLogger(It.IsAny())) - .Returns(loggerMock.Object); + protected static Task> GetWriteStateRepository( + IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .Create(TestSessionOptions.Write); + } - loggerFactoryMock - .Setup(factory => factory.AddProvider(It.IsAny())); + protected static Task> GetReadOnlyStateRepository( + IServiceScope serviceScope, bool secondary = false) + { + return serviceScope.ServiceProvider + .GetRequiredService>() + .Create(secondary ? TestSessionOptions.ReadOnlySecondaryPreferred : TestSessionOptions.ReadOnly); + } - 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 Task GetWriteSourceRepository(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .Create + ( + TestSessionOptions.Write + ); + } - return (loggerFactoryMock.Object, Verifier); + protected static Task GetReadOnlySourceRepository(IServiceScope serviceScope, + bool secondary = false) + { + return serviceScope.ServiceProvider + .GetRequiredService() + .Create + ( + secondary + ? TestSessionOptions.ReadOnlySecondaryPreferred + : TestSessionOptions.ReadOnly + ); } - protected static ITransactionRepositoryFactory GetMockedTransactionRepositoryFactory( - object[]? commands = null) + protected static Task> GetReadOnlyProjectionRepository( + IServiceScope serviceScope, bool getPersistedState) { - commands ??= Array.Empty(); + return serviceScope.ServiceProvider + .GetRequiredService>() + .CreateRepository + ( + getPersistedState ? TestSessionOptions.Write : null + ); + } - var transactionRepositoryMock = new Mock(MockBehavior.Strict); + protected static ISourceRepositoryFactory GetMockedSourceRepositoryFactory( + Mock sourceRepositoryMock, List? committedSources = null) + { + sourceRepositoryMock + .Setup(repository => repository.Commit(It.IsAny(), It.IsAny())) + .ReturnsAsync((Source source, CancellationToken _) => + { + committedSources?.Add(source); - transactionRepositoryMock - .Setup(repository => repository.PutTransaction(It.IsAny(), It.IsAny())) - .ReturnsAsync(true); + return true; + }); - transactionRepositoryMock - .Setup(repository => repository.EnumerateCommands(It.IsAny(), It.IsAny())) - .Returns(AsyncEnumerablePolyfill.FromResult(commands)); + sourceRepositoryMock + .Setup(repository => repository.Dispose()); - transactionRepositoryMock + sourceRepositoryMock .Setup(repository => repository.DisposeAsync()) .Returns(ValueTask.CompletedTask); - var transactionRepositoryFactoryMock = - new Mock(MockBehavior.Strict); + var sourceRepositoryFactoryMock = + new Mock(MockBehavior.Strict); - transactionRepositoryFactoryMock - .Setup(factory => factory.CreateRepository(It.IsAny(), It.IsAny())) - .ReturnsAsync(transactionRepositoryMock.Object); + sourceRepositoryFactoryMock + .Setup(factory => factory.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(sourceRepositoryMock.Object); - transactionRepositoryFactoryMock + sourceRepositoryFactoryMock .Setup(factory => factory.Dispose()); - return transactionRepositoryFactoryMock.Object; + 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 + 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; } - private record TestServiceScope - (ServiceProvider SingletonServiceProvider, IServiceScope ServiceScope) : IServiceScope + public sealed record Log { - public IServiceProvider ServiceProvider => ServiceScope.ServiceProvider; + 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 sealed class Logger(ICollection logs) : ILogger + { + private readonly Stack _scopeStates = new(); - public void Dispose() + IDisposable ILogger.BeginScope(TState state) { - ServiceScope.Dispose(); + _scopeStates.Push(state); - SingletonServiceProvider.Dispose(); + 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, + }); + } + + private sealed class Scope(Stack scopeStates) : IDisposable + { + void IDisposable.Dispose() + { + scopeStates.Pop(); + } + } + } + + internal sealed 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 sealed record SourceRepositoryAdder(string Name, Type QueryOptionsType, + Func FixTimeStamp, + AddDependenciesDelegate AddDependencies) + { + public override string ToString() + { + return Name; } } - public record TransactionsAdder(string Name, Type QueryOptionsType, Func FixTimeStamp, AddDependenciesDelegate AddDependencies) + public sealed record StateRepositoryAdder(string Name, Type StateType, AddDependenciesDelegate AddDependencies) { public override string ToString() { @@ -534,7 +637,7 @@ public override string ToString() } } - public record SnapshotAdder(string Name, Type SnapshotType, AddDependenciesDelegate AddDependencies) + public sealed record EntityRepositoryAdder(string Name, Type EntityType, AddDependenciesDelegate AddDependencies) { public override string ToString() { @@ -542,11 +645,12 @@ public override string ToString() } } - public record EntityAdder(string Name, Type EntityType, AddDependenciesDelegate AddDependencies) + public sealed 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/Transactions/EntitySnapshotTransactionSubscriberTests.cs b/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs deleted file mode 100644 index f4f8b5e4..00000000 --- a/test/EntityDb.Common.Tests/Transactions/EntitySnapshotTransactionSubscriberTests.cs +++ /dev/null @@ -1,125 +0,0 @@ -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; - -[Collection(nameof(DatabaseContainerCollection))] -public class EntitySnapshotTransactionSubscriberTests : TestsBase -{ - public EntitySnapshotTransactionSubscriberTests(IServiceProvider startupServiceProvider, DatabaseContainerFixture databaseContainerFixture) : base(startupServiceProvider, databaseContainerFixture) - { - } - - private async Task - Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotTransactionSubscriber_ThenAlwaysWriteSnapshot< - TEntity>( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE - - TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => true; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - entitySnapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - const uint numberOfVersionNumbers = 10; - - var transaction = TransactionSeeder.Create(entityId, numberOfVersionNumbers); - - 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.PutTransaction(transaction); - - var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); - - // ASSERT - - snapshot.ShouldNotBe(default); - snapshot!.GetVersionNumber().Value.ShouldBe(numberOfVersionNumbers); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntitySnapshots))] - private Task - GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsTrue_WhenRunningEntitySnapshotTransactionSubscriber_ThenAlwaysWriteSnapshot( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) - { - return RunGenericTestAsync - ( - new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder } - ); - } - - private async Task - Generic_GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotTransactionSubscriber_ThenNeverWriteSnapshot< - TEntity>(TransactionsAdder transactionsAdder, SnapshotAdder snapshotAdder) - where TEntity : class, IEntity, ISnapshotWithTestLogic - { - // ARRANGE - - TEntity.ShouldRecordAsLatestLogic.Value = (_, _) => false; - - using var serviceScope = CreateServiceScope(serviceCollection => - { - transactionsAdder.AddDependencies.Invoke(serviceCollection); - snapshotAdder.AddDependencies.Invoke(serviceCollection); - }); - - var entityId = Id.NewId(); - - var transaction = TransactionSeeder.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.PutTransaction(transaction); - - var snapshot = await snapshotRepository.GetSnapshotOrDefault(entityId); - - // ASSERT - - snapshot.ShouldBe(default); - } - - [Theory] - [MemberData(nameof(AddTransactionsAndEntitySnapshots))] - private Task - GivenSnapshotShouldRecordAsMostRecentAlwaysReturnsFalse_WhenRunningEntitySnapshotTransactionSubscriber_ThenNeverWriteSnapshot( - TransactionsAdder transactionsAdder, SnapshotAdder entitySnapshotAdder) - { - return RunGenericTestAsync - ( - new[] { entitySnapshotAdder.SnapshotType }, - new object?[] { transactionsAdder, entitySnapshotAdder } - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs b/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs deleted file mode 100644 index fdc3d3ab..00000000 --- a/test/EntityDb.Common.Tests/Transactions/SingleEntityTransactionBuilderTests.cs +++ /dev/null @@ -1,298 +0,0 @@ -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; -using EntityDb.Common.Leases; -using EntityDb.Common.Tests.Implementations.Commands; -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Xunit; - -namespace EntityDb.Common.Tests.Transactions; - -public class SingleEntityTransactionBuilderTests : TestsBase -{ - public SingleEntityTransactionBuilderTests(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 transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - // ASSERT - - transactionBuilder.IsEntityKnown().ShouldBeFalse(); - - Should.Throw(() => transactionBuilder.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 transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, expectedEntityId); - - transactionBuilder.Load(expectedEntity); - - // ARRANGE ASSERTIONS - - transactionBuilder.IsEntityKnown().ShouldBeTrue(); - - // ACT - - var actualEntityId = transactionBuilder.EntityId; - var actualEntity = transactionBuilder.GetEntity(); - - // ASSERT - - actualEntityId.ShouldBe(expectedEntityId); - actualEntity.ShouldBe(expectedEntity); - } - - private async Task - Generic_GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenTransactionDoesInsertLeases( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - }); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - // ACT - - var transaction = transactionBuilder - .Add(new Lease(default!, default!, default!)) - .Build(default); - - // ASSERT - - transaction.Steps.Length.ShouldBe(1); - - var leaseTransactionStep = - transaction.Steps[0].ShouldBeAssignableTo().ShouldNotBeNull(); - - leaseTransactionStep.Leases.ShouldNotBeEmpty(); - } - - 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(_ => - GetMockedTransactionRepositoryFactory( - new object[] { new DoNothing() })); - }); - - var transactionBuilder = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateForSingleEntity(default!, default); - - await using var entityRepository = await serviceScope.ServiceProvider - .GetRequiredService>() - .CreateRepository(default!); - - var entity = await entityRepository.GetSnapshot(entityId); - - // ACT - - transactionBuilder.Load(entity); - - // ASSERT - - Should.Throw(() => { transactionBuilder.Load(entity); }); - } - - private async Task - Generic_GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersionNumberAutoIncrements( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var numberOfVersionsToTest = new VersionNumber(10); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddScoped(_ => - GetMockedTransactionRepositoryFactory()); - }); - - var transactionBuilder = 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()); - - var transaction = transactionBuilder.Build(default); - - // ASSERT - - for (var v = new VersionNumber(1); v.Value <= numberOfVersionsToTest.Value; v = v.Next()) - { - var index = (int)(v.Value - 1); - - var commandTransactionStep = transaction.Steps[index].ShouldBeAssignableTo() - .ShouldNotBeNull(); - - commandTransactionStep.EntityVersionNumber.ShouldBe(v); - } - } - - private async Task Generic_GivenExistingEntity_WhenAppendingNewCommand_ThenTransactionBuilds( - EntityAdder entityAdder) - where TEntity : IEntity - { - // ARRANGE - - var entityId = Id.NewId(); - - using var serviceScope = CreateServiceScope(serviceCollection => - { - entityAdder.AddDependencies.Invoke(serviceCollection); - - serviceCollection.AddScoped(_ => - GetMockedTransactionRepositoryFactory(new object[] { new DoNothing() })); - }); - - var transactionBuilder = 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 transaction = transactionBuilder - .Load(entity) - .Append(new DoNothing()) - .Build(default); - - // ASSERT - - transaction.Steps.Length.ShouldBe(1); - - var commandTransactionStep = - transaction.Steps[0].ShouldBeAssignableTo().ShouldNotBeNull(); - - commandTransactionStep.Command.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 GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenTransactionDoesInsertLeases( - 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_WhenUsingValidVersioningStrategy_ThenVersionNumberAutoIncrements( - EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } - - [Theory] - [MemberData(nameof(AddEntity))] - public Task GivenExistingEntity_WhenAppendingNewCommand_ThenTransactionBuilds(EntityAdder entityAdder) - { - return RunGenericTestAsync - ( - new[] { entityAdder.EntityType }, - new object?[] { entityAdder } - ); - } -} \ No newline at end of file diff --git a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs b/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs deleted file mode 100644 index bb86ac11..00000000 --- a/test/EntityDb.Common.Tests/Transactions/TransactionTests.cs +++ /dev/null @@ -1,1757 +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.Transactions.Steps; -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)); - transactionBuilder.Add(new CountLease(count)); - transactionBuilder.Add(new CountTag(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 - { - Steps = Enumerable - .Repeat(transaction.Steps, repeatCount) - .SelectMany(steps => steps) - .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 transactionStepMock = new Mock(MockBehavior.Strict); - - transactionStepMock - .SetupGet(step => step.EntityId) - .Returns(default(Id)); - - transactionStepMock - .SetupGet(step => step.PreviousEntityVersionNumber) - .Returns(versionNumber); - - transactionStepMock - .SetupGet(step => step.EntityVersionNumber) - .Returns(versionNumber); - - var transaction = TransactionSeeder.Create(transactionStepMock.Object, transactionStepMock.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.Steps.Length.ShouldBe(1); - secondTransaction.Steps.Length.ShouldBe(1); - - firstTransaction.Steps.ShouldAllBe(step => step.EntityId == entityId); - secondTransaction.Steps.ShouldAllBe(step => step.EntityId == entityId); - - var firstCommandTransactionStep = firstTransaction.Steps[0] - .ShouldBeAssignableTo().ShouldNotBeNull(); - var secondCommandTransactionStep = secondTransaction.Steps[0] - .ShouldBeAssignableTo().ShouldNotBeNull(); - - firstCommandTransactionStep.EntityVersionNumber.ShouldBe(secondCommandTransactionStep.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 - .Add(tag) - .Add(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 - .Add(lease) - .Add(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 - .Add(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 - .Delete(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 - .Add(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 - .Delete(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.Steps.Length.ShouldBe(1); - - var commandTransactionStep = - transaction.Steps[0].ShouldBeAssignableTo().ShouldNotBeNull(); - - commandTransactionStep.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.Steps.Length.ShouldBe(1); - - var secondCommandTransactionStep = secondTransaction.Steps[0] - .ShouldBeAssignableTo().ShouldNotBeNull(); - - secondCommandTransactionStep.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..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() @@ -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,11 +134,11 @@ public void GivenGarbageTypeInformation_WhenLoadingType_ThenThrow() [EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform, [EnvelopeHelper.AssemblyFullName] = "Garbage", [EnvelopeHelper.TypeFullName] = "Garbage", - [EnvelopeHelper.MemberInfoName] = "Garbage" + [EnvelopeHelper.MemberInfoName] = "Garbage", }); // ASSERT 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..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) { @@ -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 d06c6b4a..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() @@ -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 }); @@ -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 48a2f446..47ccc887 100644 --- a/test/EntityDb.Common.Tests/packages.lock.json +++ b/test/EntityDb.Common.Tests/packages.lock.json @@ -4,56 +4,43 @@ "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", - "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.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "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==", - "dependencies": { - "Castle.Core": "5.1.0" - } - }, - "Npgsql.EntityFrameworkCore.PostgreSQL": { - "type": "Direct", - "requested": "[7.0.0, )", - "resolved": "7.0.0", - "contentHash": "CyUNlFZmtX2Kmw8XK5Tlx5eVUCzWJ+zJHErxZiMo2Y8zCRuH9+/OMGwG+9Mmp5zD5p3Ifbi5Pp3btsqoDDkSZQ==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "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" + "Castle.Core": "5.1.1" } }, "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,74 +52,95 @@ "Microsoft.Bcl.AsyncInterfaces": "6.0.0" } }, - "Testcontainers": { + "Testcontainers.MongoDb": { + "type": "Direct", + "requested": "[3.7.0, )", + "resolved": "3.7.0", + "contentHash": "Cy8dlgvZSW3U6rrmLKwoiAnPpy167rAAKFVGfAPNwX358UJNrlW9isnxFDcAWwhYTogS26DNnRXx3RKiUQOOWQ==", + "dependencies": { + "Testcontainers": "3.7.0" + } + }, + "Testcontainers.Redis": { "type": "Direct", - "requested": "[2.2.0, )", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "requested": "[3.7.0, )", + "resolved": "3.7.0", + "contentHash": "Lf16QdS1MbxtCat6G3LIKSywlv79ht5pFb08eVojIZE2eGKBoOcf9zkDVeBf7guxddWF1ZN0P3lQVLqRIZTTog==", "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" + "Testcontainers": "3.7.0" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "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", - "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.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.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, "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 +153,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -155,82 +163,36 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "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==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "Microsoft.EntityFrameworkCore.Relational": { + "MartinCostello.Logging.XUnit": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" } }, - "Microsoft.Extensions.Caching.Abstractions": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "Microsoft.Extensions.Caching.Memory": { + "Microsoft.CodeCoverage": { "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" - } + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -243,10 +205,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": { @@ -312,16 +274,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", @@ -386,19 +349,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.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -466,11 +430,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": { @@ -487,8 +451,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", @@ -502,19 +469,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "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.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -537,52 +504,46 @@ "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.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.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "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.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "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.18.0", - "MongoDB.Libmongocrypt": "1.6.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": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -640,33 +601,19 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "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" } }, - "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", @@ -781,21 +728,34 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "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.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -813,8 +773,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 +816,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", @@ -900,17 +851,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 +871,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 +1021,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 +1386,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 +1418,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 +1454,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "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" @@ -1582,14 +1503,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 +1544,21 @@ "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", @@ -1638,30 +1566,27 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1669,17 +1594,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "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", @@ -1694,68 +1619,1688 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.entityframework": { + "entitydb.json": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.inmemory": { + "entitydb.mongodb": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.json": { + "entitydb.provisioner": { "type": "Project", "dependencies": { - "EntityDb.Common": "[1.0.0, )", + "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.mongodb": { + "entitydb.redis": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.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==" }, - "entitydb.npgsql": { - "type": "Project", + "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": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", - "System.Linq.Async": "[6.0.1, )" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, - "entitydb.provisioner": { - "type": "Project", + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "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, )" + "Castle.Core": "5.1.1" } }, - "entitydb.redis": { - "type": "Project", + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", - "System.Linq.Async": "[6.0.1, )" + "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]" } }, - "entitydb.sqldb": { + "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, )" + } + }, + "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, )" } } diff --git a/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj b/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj index debf235b..8577fd3b 100644 --- a/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj +++ b/test/EntityDb.MongoDb.Tests/EntityDb.MongoDb.Tests.csproj @@ -1,18 +1,8 @@  - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + diff --git a/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs b/test/EntityDb.MongoDb.Tests/Envelopes/BsonDocumentEnvelopeTests.cs index 60be64cb..1e798157 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; @@ -10,7 +10,7 @@ namespace EntityDb.MongoDb.Tests.Envelopes; -public class BsonDocumentEnvelopeTests : EnvelopeTestsBase +public sealed class BsonDocumentEnvelopeTests : EnvelopeTestsBase { public BsonDocumentEnvelopeTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { @@ -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)); @@ -46,10 +46,11 @@ 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 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 c1d593e6..d550a4ef 100644 --- a/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs +++ b/test/EntityDb.MongoDb.Tests/Sessions/MongoSessionTests.cs @@ -1,12 +1,12 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests; -using EntityDb.MongoDb.Sessions; +using EntityDb.MongoDb.Sources.Sessions; using Shouldly; using Xunit; namespace EntityDb.MongoDb.Tests.Sessions; -public class MongoSessionTests : TestsBase +public sealed class MongoSessionTests : TestsBase { public MongoSessionTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { @@ -17,23 +17,21 @@ public async Task WhenExecutingWriteMethods_ThenThrow() { // ARRANGE - var mongoSession = new MongoSession(default!, default!, default!, new MongoDbTransactionSessionOptions - { - 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..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) { @@ -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 ddbc98af..67587266 100644 --- a/test/EntityDb.MongoDb.Tests/packages.lock.json +++ b/test/EntityDb.MongoDb.Tests/packages.lock.json @@ -4,44 +4,43 @@ "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", - "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.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "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": { "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,57 +54,75 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "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", - "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.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.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, "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 +135,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -128,82 +145,36 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "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==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "Microsoft.EntityFrameworkCore.Relational": { + "MartinCostello.Logging.XUnit": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" } }, - "Microsoft.Extensions.Caching.Abstractions": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "Microsoft.Extensions.Caching.Memory": { + "Microsoft.CodeCoverage": { "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" - } + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -216,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": { @@ -285,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", @@ -359,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.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -439,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": { @@ -460,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", @@ -475,19 +451,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "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.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -510,52 +486,46 @@ "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.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.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "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.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "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.18.0", - "MongoDB.Libmongocrypt": "1.6.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": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -613,44 +583,19 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "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", - "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" } }, - "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", @@ -765,21 +710,34 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "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.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -797,8 +755,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 +798,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", @@ -884,17 +833,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 +853,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 +1003,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 +1368,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 +1400,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 +1436,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "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" @@ -1566,14 +1485,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 +1528,33 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "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": { - "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" + "Testcontainers": "3.7.0" } }, "xunit.abstractions": { @@ -1636,30 +1564,27 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1667,17 +1592,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "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", @@ -1695,85 +1620,1719 @@ "entitydb.common.tests": { "type": "Project", "dependencies": { - "Bogus": "[34.0.2, )", + "Bogus": "[35.3.0, )", "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, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", + "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.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.6.5, )" } }, - "entitydb.entityframework": { + "entitydb.json": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.inmemory": { + "entitydb.mongodb": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.json": { + "entitydb.provisioner": { "type": "Project", "dependencies": { - "EntityDb.Common": "[1.0.0, )", + "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.mongodb": { + "entitydb.redis": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.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==" }, - "entitydb.npgsql": { - "type": "Project", + "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": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", - "System.Linq.Async": "[6.0.1, )" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, - "entitydb.provisioner": { - "type": "Project", + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "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, )" + "Castle.Core": "5.1.1" } }, - "entitydb.redis": { - "type": "Project", + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", - "System.Linq.Async": "[6.0.1, )" + "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" } }, - "entitydb.sqldb": { + "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" + } + }, + "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.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, )" } } diff --git a/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs b/test/EntityDb.Mvc.Tests/Agents/HttpContextAgentAccessorTests.cs index 8a27a3a6..99572c8f 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; @@ -7,7 +7,7 @@ namespace EntityDb.Mvc.Tests.Agents; -public class HttpContextAgentAccessorTests : AgentAccessorTestsBase +public sealed class HttpContextAgentAccessorTests : AgentAccessorTestsBase { public HttpContextAgentAccessorTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { @@ -44,27 +44,21 @@ protected override IEnumerable GetAgentAccessorOptions { new HttpContextSeederOptions { - Headers = new Dictionary - { - ["Content-Type"] = new[] { "application/json" } - }, - HasIpAddress = true + Headers = new Dictionary { ["Content-Type"] = new[] { "application/json" } }, + HasIpAddress = true, }, new HttpContextSeederOptions { - Headers = new Dictionary - { - ["Content-Type"] = new[] { "application/json" } - }, - HasIpAddress = false - } + Headers = new Dictionary { ["Content-Type"] = new[] { "application/json" } }, + HasIpAddress = false, + }, }; } protected override Dictionary? GetApplicationInfo(object agentSignature) { - return agentSignature is not HttpContextAgentSignature.Snapshot httpContextAgentSignature + return agentSignature is not HttpContextAgentSignature httpContextAgentSignature ? 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 7c12431e..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() @@ -15,29 +15,23 @@ 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 - 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] @@ -51,28 +45,24 @@ 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 - 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 +76,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] @@ -121,27 +111,26 @@ public void var httpContextAgentOptions = new HttpContextAgentSignatureOptions { - RedactedQueryStringParams = new[] { queryStringParamName }, - RedactedValue = redactedValue + RedactedQueryStringParams = new[] { queryStringParamName }, 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 cf8c57f4..6f7592b4 100644 --- a/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj +++ b/test/EntityDb.Mvc.Tests/EntityDb.Mvc.Tests.csproj @@ -1,18 +1,8 @@  - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + 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 6eadad4c..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; @@ -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(); @@ -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..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(); @@ -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..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) { @@ -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 866d07aa..a40c469f 100644 --- a/test/EntityDb.Mvc.Tests/packages.lock.json +++ b/test/EntityDb.Mvc.Tests/packages.lock.json @@ -4,44 +4,43 @@ "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", - "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.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "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": { "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,57 +54,75 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "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", - "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.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.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, "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 +135,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -128,82 +145,36 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "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==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "Microsoft.EntityFrameworkCore.Relational": { + "MartinCostello.Logging.XUnit": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" } }, - "Microsoft.Extensions.Caching.Abstractions": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "Microsoft.Extensions.Caching.Memory": { + "Microsoft.CodeCoverage": { "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" - } + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -216,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": { @@ -285,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", @@ -359,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.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -439,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": { @@ -460,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", @@ -475,19 +451,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "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.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -510,52 +486,46 @@ "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.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.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "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.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "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.18.0", - "MongoDB.Libmongocrypt": "1.6.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": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -613,44 +583,19 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "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", - "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" } }, - "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", @@ -765,21 +710,34 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "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.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -797,8 +755,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 +798,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", @@ -884,17 +833,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 +853,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 +1003,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 +1368,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 +1400,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 +1436,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "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" @@ -1566,14 +1485,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 +1528,33 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "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": { - "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" + "Testcontainers": "3.7.0" } }, "xunit.abstractions": { @@ -1636,30 +1564,27 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1667,17 +1592,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "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", @@ -1695,36 +1620,19 @@ "entitydb.common.tests": { "type": "Project", "dependencies": { - "Bogus": "[34.0.2, )", + "Bogus": "[35.3.0, )", "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, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", + "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, )" - } - }, - "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": { - "EntityDb.Common": "[1.0.0, )", - "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": { @@ -1738,7 +1646,7 @@ "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1749,19 +1657,10 @@ "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.npgsql": { - "type": "Project", - "dependencies": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", - "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, )" @@ -1772,15 +1671,1682 @@ "dependencies": { "EntityDb.Common": "[1.0.0, )", "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", + "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)" + } }, - "entitydb.sqldb": { + "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" + } + }, + "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.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, )" } } diff --git a/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj b/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj index b4c9f0de..2d9d164d 100644 --- a/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj +++ b/test/EntityDb.Redis.Tests/EntityDb.Redis.Tests.csproj @@ -1,18 +1,8 @@  - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + diff --git a/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs b/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs index f56b3976..0be5620e 100644 --- a/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs +++ b/test/EntityDb.Redis.Tests/Envelopes/JsonElementEnvelopeTests.cs @@ -1,9 +1,9 @@ -using System.Text; -using EntityDb.Common.Tests.Envelopes; +using EntityDb.Common.Tests.Envelopes; +using System.Text; namespace EntityDb.Redis.Tests.Envelopes; -public class JsonElementEnvelopeTests : EnvelopeTestsBase +public sealed class JsonElementEnvelopeTests : EnvelopeTestsBase { public JsonElementEnvelopeTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { @@ -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 eb341fc1..c9dc3aaa 100644 --- a/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs +++ b/test/EntityDb.Redis.Tests/Sessions/RedisSessionTests.cs @@ -1,12 +1,12 @@ using EntityDb.Common.Exceptions; using EntityDb.Common.Tests; -using EntityDb.Redis.Sessions; +using EntityDb.Redis.States.Sessions; using Shouldly; using Xunit; namespace EntityDb.Redis.Tests.Sessions; -public class RedisSessionTests : TestsBase +public sealed class RedisSessionTests : TestsBase { public RedisSessionTests(IServiceProvider startupServiceProvider) : base(startupServiceProvider) { @@ -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..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) { @@ -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 ddbc98af..67587266 100644 --- a/test/EntityDb.Redis.Tests/packages.lock.json +++ b/test/EntityDb.Redis.Tests/packages.lock.json @@ -4,44 +4,43 @@ "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", - "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.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "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": { "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,57 +54,75 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.6.5, )", + "resolved": "2.6.5", + "contentHash": "iPSL63kw21BdSsdA79bvbVNvyn17DWI4D6VbgNxYtvzgViKrmbRLr8sWPxSlc4AvnofEuFfAi/rrLSzSRomwCg==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.9.0", + "xunit.assert": "2.6.5", + "xunit.core": "[2.6.5]" } }, "Xunit.DependencyInjection": { "type": "Direct", - "requested": "[8.6.1, )", - "resolved": "8.6.1", - "contentHash": "O0d2fGId3gD67ZoVKDSu08e6wBXSl0aP54J6TdVGzwf6chTH9GBPBg3y6aLjp9wYfwHNPVaE41+0brM64tbbnA==", + "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", - "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.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.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, "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 +135,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -128,82 +145,36 @@ }, "Docker.DotNet.X509": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "CwxE5BUJNdKvhLD2NZuxzGd2y6o+grkIJQr3JavEUILTlJd0tZMVDfge63wWTuzsbq4sTEhE4bZBLEgG/0vzgw==", + "resolved": "3.125.15", + "contentHash": "ONQN7ImrL3tHStUUCCPHwrFFQVpIpE+7L6jaDAMwSF+yTEmeWBmRARQZDRuvfj/+WtB8RR0oTW0tT3qQMSyHOw==", "dependencies": { - "Docker.DotNet": "3.125.12" + "Docker.DotNet": "3.125.15" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "2N6IdrlSYT+FhX5hAbasJ7wpV/ONtIX+7fN+XXukwGAgHRNjiAoO0TScQsTZcgaXmbuvGu4ogKk0jPt2dVLgTQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.CSharp": { - "type": "Transitive", - "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==" + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" }, - "Microsoft.EntityFrameworkCore.Relational": { + "MartinCostello.Logging.XUnit": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "eQiYygtR2xZ0Uy7KtiFRHpoEx/U8xNwbNRgu1pEJgSxbJLtg6tDL1y2YcIbSuIRSNEljXIIHq/apEhGm1QL70g==", + "resolved": "0.3.0", + "contentHash": "p6SWKQRLXEqYqnzA7mulCPfdZraDXFc7gHCErj1uw9KTNi4agFZqFDCANIfwAJ7ivWlAUAFZDTDFxb4cAhkPlw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "7.0.0" + "Microsoft.Extensions.Logging": "2.0.0", + "xunit.abstractions": "2.0.2", + "xunit.extensibility.execution": "2.4.0" } }, - "Microsoft.Extensions.Caching.Abstractions": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "IeimUd0TNbhB4ded3AbgBLQv2SnsiVugDyGV1MvspQFVlA07nDC7Zul7kcwH5jWN3JiTcp/ySE83AIJo8yfKjg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "7.0.0" - } + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" }, - "Microsoft.Extensions.Caching.Memory": { + "Microsoft.CodeCoverage": { "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" - } + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -216,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": { @@ -285,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", @@ -359,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.4", + "contentHash": "K14wYgwOfKVELrUh5eBqlC8Wvo9vvhS3ZhIvcswV2uS/ubkTRPSQsN557EZiYUSSoZNxizG+alN4wjtdyLdcyw==" }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -439,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": { @@ -460,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", @@ -475,19 +451,19 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", "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.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.8.0", "Newtonsoft.Json": "13.0.1" } }, @@ -510,52 +486,46 @@ "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.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.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.23.1", + "contentHash": "kidqCwGBuLBx2IcW4os3J6zsp9yaUWm7Sp8G08Nm2RVRSAf0cJXfsynl2wRWpHh0HgfIzzwkevP/qhfsKfu8bQ==", "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.23.1", + "MongoDB.Driver.Core": "2.23.1", + "MongoDB.Libmongocrypt": "1.8.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "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.18.0", - "MongoDB.Libmongocrypt": "1.6.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": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.8.0", + "contentHash": "fgNw8Dxpkq7mpoaAYes8cfnPRzvFIoB8oL9GPXwi3op/rONftl0WAeg4akRLcxfoVuUvuUO2wGoVBr3JzJ7Svw==" }, "NETStandard.Library": { "type": "Transitive", @@ -613,44 +583,19 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Npgsql": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "tOBFksJZ2MiEz8xtDUgS5IG19jVO3nSP15QDYWiiGpXHe0PsLoQBts2Sg3hHKrrLTuw+AjsJz9iKvvGNHyKDIg==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "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", - "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" } }, - "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", @@ -765,21 +710,34 @@ }, "SharpZipLib": { "type": "Transitive", - "resolved": "1.4.0", - "contentHash": "CdkbBSPIpHD8xBlu+8kDJiqc1Tf9iV89BObnqcvEbwysXSj5h1MfaeLgeeaxPZmi7CTJO8FDofBBNxBW0Vml7A==" + "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.6.70", - "contentHash": "O1QpPNrcGZXXClqdNe69/ySwRPINTwSnZQr0qxKfBMUPqemmJ5UXVwCznVIwMEYhoBlZtIgC+ZYywhNb8oXaKg==", + "resolved": "2.7.10", + "contentHash": "DHC438nKZUbtJ0H8O5pCTMrurYy6ByUPhzUNAPCUADtI4BSyvASpWAquYjN+4NKEiKaW5AO++tgftum3tDraPQ==", "dependencies": { - "Pipelines.Sockets.Unofficial": "2.2.2", - "System.Diagnostics.PerformanceCounter": "5.0.0" + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" } }, "System.AppContext": { @@ -797,8 +755,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 +798,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", @@ -884,17 +833,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 +853,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 +1003,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 +1368,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 +1400,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 +1436,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "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" @@ -1566,14 +1485,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 +1528,33 @@ }, "Testcontainers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "mqzDSr51t9g+gHUy7KYij6EQ0ixofqjq8GtO8nfIPSf/Xohdl0vgd3z/WB0AFprXXDYVpwSVXyEtYqo4EXPRpQ==", + "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": { - "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" + "Testcontainers": "3.7.0" } }, "xunit.abstractions": { @@ -1636,30 +1564,27 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.9.0", + "contentHash": "02ucFDty6Y9BBT5c35YueFfbM3uEzeFdRvlNtAPhZVUkGUlhl3jsV2XesgTj986/PZXIjpVoc2D8ee6p1ha/Fw==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.6.5", + "contentHash": "gb5uv7vjBFz7nhEa6aXK5sVJwsG/88xf8DN5wqK0ejCDsDybqICyNJIj+eoD43xbmdPZryNDPpeWDCfiKI/bnA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.6.5", + "contentHash": "hpdMnSNlx4ejaxpaIAFaqHt4q9ZCnzZLnURrSa5CzYXxHhIQbV8/0yXLjRdublhreonGXVMmsQ1KHlS9WbfpCw==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.6.5]", + "xunit.extensibility.execution": "[2.6.5]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.6.5", + "contentHash": "dSGRkVxzH27XaL83+Z9kNPllqgsmsiPayXw+0weCGsrZQxfSCBNNkSb9nYUpkVoEBCUviXOmo1tfApqhgqTjog==", "dependencies": { "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" @@ -1667,17 +1592,17 @@ }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.6.5", + "contentHash": "jUMr88e0lSqDq8Vut0XVqx7plFg91QsKW/rX6gaVnJL6Z19LmNSDmyqd7cg6HQGfboAmyoFZyydA4Kcgouu1BA==", "dependencies": { "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "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", @@ -1695,85 +1620,1719 @@ "entitydb.common.tests": { "type": "Project", "dependencies": { - "Bogus": "[34.0.2, )", + "Bogus": "[35.3.0, )", "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, )", + "Microsoft.NET.Test.Sdk": "[17.8.0, )", + "Moq": "[4.20.70, )", + "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.7.0, )", + "Testcontainers.Redis": "[3.7.0, )", + "Xunit.DependencyInjection": "[8.9.1, )", + "Xunit.DependencyInjection.Logging": "[8.1.0, )", + "xunit": "[2.6.5, )" } }, - "entitydb.entityframework": { + "entitydb.json": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.Relational": "[7.0.0, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.inmemory": { + "entitydb.mongodb": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", + "MongoDB.Driver": "[2.23.1, )", "System.Linq.Async": "[6.0.1, )" } }, - "entitydb.json": { + "entitydb.provisioner": { "type": "Project", "dependencies": { - "EntityDb.Common": "[1.0.0, )", + "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.mongodb": { + "entitydb.redis": { "type": "Project", "dependencies": { "EntityDb.Common": "[1.0.0, )", - "MongoDB.Driver": "[2.18.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==" }, - "entitydb.npgsql": { - "type": "Project", + "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": { - "EntityDb.SqlDb": "[1.0.0, )", - "Npgsql": "[7.0.0, )", - "System.Linq.Async": "[6.0.1, )" + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" } }, - "entitydb.provisioner": { - "type": "Project", + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "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, )" + "Castle.Core": "5.1.1" } }, - "entitydb.redis": { - "type": "Project", + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", "dependencies": { - "EntityDb.Common": "[1.0.0, )", - "EntityDb.Json": "[1.0.0, )", - "StackExchange.Redis": "[2.6.70, )", - "System.Linq.Async": "[6.0.1, )" + "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" } }, - "entitydb.sqldb": { + "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" + } + }, + "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.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, )" } }