Skip to content

Commit 80659fe

Browse files
Feature: Projections (#27)
* feat: abstractions for projections * chore: add some doc comments * wip: checkpoint * refactor: rebase on feat/better-reducer * refactor: not nullable, will never be null * bugfix: add missing base class * Delete IStatement.cs * checkpoint Co-Authored-By: watterssn <[email protected]> * chore: todos for better way to update the cache * refactor: account for changes from #29 * feat: finish implementations first pass * refactor: rename TransactionEntity to TestEntity (no such thing as non-transaction entity) * refactor: rename variables to entity snapshots to be distinct from single entity projection snapshots * test: add entity snapshot subscriber for all entity snapshot tests * bugfix: didn't mean to commit this yet * refactor: better name * test: generalize snapshot tests to work for projection snapshots as well * chore: store entity type * chore: add report generator for easily drilling down through coverage * chore: clean up coverage a little * refactor: remove unused method * wip: coverage test * test: add coverage for projection repository/subscriber Co-authored-by: watterssn <[email protected]>
1 parent 4c4eb71 commit 80659fe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1137
-177
lines changed

.config/dotnet-tools.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": 1,
3+
"isRoot": true,
4+
"tools": {
5+
"dotnet-reportgenerator-globaltool": {
6+
"version": "5.1.2",
7+
"commands": [
8+
"reportgenerator"
9+
]
10+
}
11+
}
12+
}

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
TestResults
2-
CoverageResults
2+
CoverageReport
33

44
## Ignore Visual Studio temporary files, build results, and
55
## files generated by popular Visual Studio add-ons.

generate-coverage-report.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/sh
2+
rm -rf ./TestResults ./CoverageReport
3+
dotnet tool restore
4+
dotnet restore EntityDb.sln --locked-mode
5+
#dotnet test EntityDb.sln --no-restore -c Debug --collect:"XPlat Code Coverage" -r ./TestResults -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
6+
#dotnet reportgenerator -reports:"./TestResults/**/coverage.opencover.xml" -targetdir:"CoverageReport" -reporttypes:Html -license:"ew0KICAiTGljZW5zZSI6IHsNCiAgICAiSWQiOiAiZDZkYjQ2NTYtMjUyMS00MjNlLWE0MTgtZmU2NjJiNDZiMDk3IiwNCiAgICAiTG9naW4iOiAidGhlLWF2aWQtZW5naW5lZXIiLA0KICAgICJOYW1lIjogIkNocmlzIFBoaWxpcHMiLA0KICAgICJFbWFpbCI6IG51bGwsDQogICAgIkxpY2Vuc2VUeXBlIjogIlBybyIsDQogICAgIklzc3VlZEF0IjogIjIwMjItMDMtMTlUMDA6MjA6MTYuNDQ2OTg3MVoiDQogIH0sDQogICJTaWduYXR1cmUiOiAidmZtTldaN2VVeTZjbVpaUFVMS2hCUzBSYVAzNVFjdlNRMWdEbU1PWTFhenlsTlpUVDNEUjl0bWptZDc3RTZVaTAycFZOWDA2YTdCMzc4ZHV6NHZ0TFdFOWg2VDNOMUhXWTJwXHUwMDJCN0FYbVlqc1x1MDAyQmdTbVNud0tueWFiNjJ5dHhLd3Y0TWdLbjh2OHF4aXlCOVBJMGV4YkJPTE11VXAwWVQ4WDc0YW9teWhEam5aWm9kbXhrN05zYzllQTBxRnBFaEZ0QkEzRzNFSWdcdTAwMkJNNHVPb002VEtNMERaV2g1NGFsVHloU25QS2ZNSjRuMWp1OWxGaHlHektDaXhOcUJhTk5LSVl6UnFEWmhxbEpYSGFBQmM4RnNGVlNWVnBwQTN6dUR3aGxxYmxHZGhOVFpTM0w2Y1FyNXNrMS9RV1ZpNDYwbXFnVWNlSVx1MDAyQmI3R2d5eFFCM3AyUG9ZQ1FFREE9PSINCn0="
7+
dotnet test EntityDb.sln --no-restore -c Debug --collect:"XPlat Code Coverage" -r ./TestResults
8+
dotnet reportgenerator -reports:"./TestResults/**/coverage.cobertura.xml" -targetdir:"CoverageReport" -reporttypes:Html -license:"ew0KICAiTGljZW5zZSI6IHsNCiAgICAiSWQiOiAiZDZkYjQ2NTYtMjUyMS00MjNlLWE0MTgtZmU2NjJiNDZiMDk3IiwNCiAgICAiTG9naW4iOiAidGhlLWF2aWQtZW5naW5lZXIiLA0KICAgICJOYW1lIjogIkNocmlzIFBoaWxpcHMiLA0KICAgICJFbWFpbCI6IG51bGwsDQogICAgIkxpY2Vuc2VUeXBlIjogIlBybyIsDQogICAgIklzc3VlZEF0IjogIjIwMjItMDMtMTlUMDA6MjA6MTYuNDQ2OTg3MVoiDQogIH0sDQogICJTaWduYXR1cmUiOiAidmZtTldaN2VVeTZjbVpaUFVMS2hCUzBSYVAzNVFjdlNRMWdEbU1PWTFhenlsTlpUVDNEUjl0bWptZDc3RTZVaTAycFZOWDA2YTdCMzc4ZHV6NHZ0TFdFOWg2VDNOMUhXWTJwXHUwMDJCN0FYbVlqc1x1MDAyQmdTbVNud0tueWFiNjJ5dHhLd3Y0TWdLbjh2OHF4aXlCOVBJMGV4YkJPTE11VXAwWVQ4WDc0YW9teWhEam5aWm9kbXhrN05zYzllQTBxRnBFaEZ0QkEzRzNFSWdcdTAwMkJNNHVPb002VEtNMERaV2g1NGFsVHloU25QS2ZNSjRuMWp1OWxGaHlHektDaXhOcUJhTk5LSVl6UnFEWmhxbEpYSGFBQmM4RnNGVlNWVnBwQTN6dUR3aGxxYmxHZGhOVFpTM0w2Y1FyNXNrMS9RV1ZpNDYwbXFnVWNlSVx1MDAyQmI3R2d5eFFCM3AyUG9ZQ1FFREE9PSINCn0="
9+
open CoverageReport/index.html
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using EntityDb.Abstractions.Disposables;
2+
using EntityDb.Abstractions.Snapshots;
3+
using EntityDb.Abstractions.Transactions;
4+
using EntityDb.Abstractions.ValueObjects;
5+
using System.Threading.Tasks;
6+
7+
namespace EntityDb.Abstractions.Projections;
8+
9+
/// <summary>
10+
/// Encapsulates the snapshot repository for a projection.
11+
/// </summary>
12+
/// <typeparam name="TProjection">The type of the projection.</typeparam>
13+
public interface IProjectionRepository<TProjection> : IDisposableResource
14+
{
15+
/// <summary>
16+
/// The strategy for mapping between projection id and entity id.
17+
/// </summary>
18+
IProjectionStrategy<TProjection> ProjectionStrategy { get; }
19+
20+
/// <summary>
21+
/// The backing transaction repository.
22+
/// </summary>
23+
ITransactionRepository TransactionRepository { get; }
24+
25+
/// <summary>
26+
/// The backing snapshot repository.
27+
/// </summary>
28+
ISnapshotRepository<TProjection> SnapshotRepository { get; }
29+
30+
/// <summary>
31+
/// Returns the current state of a <typeparamref name="TProjection" />.
32+
/// </summary>
33+
/// <param name="projectionId">The id of the projection.</param>
34+
/// <returns>The current state of a <typeparamref name="TProjection" />.</returns>
35+
Task<TProjection> GetCurrent(Id projectionId);
36+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Threading.Tasks;
2+
3+
namespace EntityDb.Abstractions.Projections;
4+
5+
/// <summary>
6+
/// Represents a type used to create instances of <see cref="IProjectionRepository{TProjection}" />
7+
/// </summary>
8+
/// <typeparam name="TProjection">The type of projection managed by the <see cref="IProjectionRepository{TProjection}" />.</typeparam>
9+
public interface IProjectionRepositoryFactory<TProjection>
10+
{
11+
/// <summary>
12+
/// Create a new instance of <see cref="IProjectionRepository{TProjection}" />
13+
/// </summary>
14+
/// <param name="transactionSessionOptionsName">The agent's use case for the transaction repository.</param>
15+
/// <param name="snapshotSessionOptionsName">The agent's use case for the snapshot repository.</param>
16+
/// <returns>A new instance of <see cref="IProjectionRepository{TProjection}" />.</returns>
17+
Task<IProjectionRepository<TProjection>> CreateRepository(string transactionSessionOptionsName,
18+
string snapshotSessionOptionsName);
19+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using EntityDb.Abstractions.ValueObjects;
2+
using System.Threading.Tasks;
3+
4+
namespace EntityDb.Abstractions.Projections;
5+
6+
/// <summary>
7+
/// Represents a type that can map a projection it to a set of entity ids.
8+
/// </summary>
9+
/// <typeparam name="TProjection"></typeparam>
10+
public interface IProjectionStrategy<in TProjection>
11+
{
12+
/// <summary>
13+
/// Map a projection id to a set of entity ids.
14+
/// </summary>
15+
/// <param name="projectionId">The id of the projection.</param>
16+
/// <param name="projectionSnapshot">A snapshot of the projection, if one exists. (This can be used to avoid running a query, if one were necessary.)</param>
17+
/// <returns>The set of entity ids to query for running the projection.</returns>
18+
Task<Id[]> GetEntityIds(Id projectionId, TProjection projectionSnapshot);
19+
20+
/// <summary>
21+
/// Map an entity id to a set of projection ids.
22+
/// </summary>
23+
/// <param name="entityId">The id of th entity.</param>
24+
/// <returns>The set of projection ids to query for running the projection.</returns>
25+
Task<Id[]> GetProjectionIds(Id entityId);
26+
}

src/EntityDb.Common/Annotations/EntityAnnotation.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using EntityDb.Abstractions.Annotations;
2+
using EntityDb.Abstractions.Transactions;
3+
using EntityDb.Abstractions.Transactions.Steps;
24
using EntityDb.Abstractions.ValueObjects;
35

46
namespace EntityDb.Common.Annotations;
@@ -10,4 +12,18 @@ internal record EntityAnnotation<TData>
1012
Id EntityId,
1113
VersionNumber EntityVersionNumber,
1214
TData Data
13-
) : IEntityAnnotation<TData>;
15+
) : IEntityAnnotation<TData>
16+
{
17+
public static EntityAnnotation<TData> CreateFrom(ITransaction transaction, ITransactionStep transactionStep,
18+
TData data)
19+
{
20+
return new
21+
(
22+
transaction.Id,
23+
transaction.TimeStamp,
24+
transactionStep.EntityId,
25+
transactionStep.EntityVersionNumber,
26+
data
27+
);
28+
}
29+
}

src/EntityDb.Common/Disposables/DisposableResourceBaseClass.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using EntityDb.Abstractions.Disposables;
2+
using System.Diagnostics.CodeAnalysis;
23
using System.Threading.Tasks;
34

45
namespace EntityDb.Common.Disposables;
56

67
internal class DisposableResourceBaseClass : IDisposableResource
78
{
9+
[ExcludeFromCodeCoverage(Justification = "All Tests Use DisposeAsync")]
810
public virtual void Dispose()
911
{
1012
DisposeAsync().AsTask().Wait();

src/EntityDb.Common/Disposables/DisposableResourceBaseRecord.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using EntityDb.Abstractions.Disposables;
2+
using System.Diagnostics.CodeAnalysis;
23
using System.Threading.Tasks;
34

45
namespace EntityDb.Common.Disposables;
56

67
internal record DisposableResourceBaseRecord : IDisposableResource
78
{
9+
[ExcludeFromCodeCoverage(Justification = "All Tests Use DisposeAsync")]
810
public virtual void Dispose()
911
{
1012
DisposeAsync().AsTask().Wait();

src/EntityDb.Common/Entities/IEntity.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ public interface IEntity<out TEntity>
1515
/// <returns>A new instance of <typeparamref name="TEntity" />.</returns>
1616
abstract static TEntity Construct(Id entityId);
1717

18+
/// <summary>
19+
/// Returns the id of the entity.
20+
/// </summary>
21+
/// <returns>The id of this entity.</returns>
22+
Id GetId();
23+
1824
/// <summary>
1925
/// Returns the version number of the entity.
2026
/// </summary>
21-
/// <returns></returns>
27+
/// <returns>The id of this entity.</returns>
2228
VersionNumber GetVersionNumber();
2329

2430
/// <summary>

0 commit comments

Comments
 (0)