Skip to content

Commit a8863f3

Browse files
Refactor: Better Transaction Subscriber (#49)
* refactor: better transaction subscriber introduces a concept of ITransactionProcess which handles the logic of processing a transaction, and does nothing else. allows the queueing logic to be abstracted away and code coverage the be better * Empty commit * Empty commit * chore: use reportgenerator github action generate new license key and store as a secret * chore: remove dead file * refactor: attempt to reduce cyclomatic complexity * chore: publish to myget before running coverage tools * chore: packages & symbols artifact? * bugfix: glob pattern is backwards * chore: do package artifact before running coverage * chore: change release process manually tagging no more, just create a release and choose a tag which is a version number
1 parent e106b9a commit a8863f3

23 files changed

+551
-474
lines changed

.config/dotnet-tools.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

.github/workflows/create-release-draft.yml

Lines changed: 0 additions & 57 deletions
This file was deleted.

.github/workflows/publish-beta.yml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,32 @@ jobs:
5151
run: dotnet restore EntityDb.sln --locked-mode
5252
- name: Run Project Tests
5353
run: dotnet test EntityDb.sln --no-restore -c Debug --collect:"XPlat Code Coverage" -r ./TestResults -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
54-
- name: Publish Coverage Results
54+
- name: Pack Projects into Nuget Packages
55+
run: dotnet pack EntityDb.sln --no-restore -c Release /p:Version='${{ github.event.pull_request.milestone.title }}-beta.${{ github.event.number }}.${{ github.run_number }}.${{ github.run_attempt }}'
56+
- name: Publish to Beta
57+
run: dotnet nuget push ./**/*.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
58+
- name: Packages & Symbols Artifact
59+
uses: actions/[email protected]
60+
with:
61+
name: Packages & Symbols
62+
path: |
63+
./**/*.nupkg
64+
./**/*.snupkg
65+
- name: Publish Coverage Results to Codacy
5566
uses: codacy/codacy-coverage-reporter-action@v1
5667
with:
5768
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
5869
coverage-reports: ./TestResults/**/*.xml
59-
- name: Pack Projects into Nuget Packages
60-
run: dotnet pack EntityDb.sln --no-restore -c Release /p:Version='${{ github.event.pull_request.milestone.title }}-beta.${{ github.event.number }}.${{ github.run_number }}.${{ github.run_attempt }}'
61-
- name: Publish to Beta
62-
run: dotnet nuget push */**.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
70+
- name: Generate Coverage Report
71+
uses: danielpalme/[email protected]
72+
with:
73+
reports: './TestResults/**/coverage.opencover.xml'
74+
targetdir: 'CoverageReport'
75+
reporttypes: 'HtmlInline'
76+
license: ${{ secrets.REPORTGENERATOR_LICENSE }}
77+
toolpath: 'reportgeneratortool'
78+
- name: Coverage Report Artifact
79+
uses: actions/[email protected]
80+
with:
81+
name: CoverageReport
82+
path: CoverageReport

.github/workflows/publish-stable.yml

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,41 @@ on:
66
name: Publish to Stable
77

88
jobs:
9-
extract:
10-
name: Extract Release Information
9+
tag:
10+
name: Try Parsing Tag
1111
runs-on: ubuntu-latest
1212
outputs:
13-
version: ${{ steps.get_release.outputs.tag_name }}
13+
is_semantic_version: ${{ steps.parse.outputs.is_semantic_version }}
1414
steps:
1515
- id: get_release
1616
uses: bruceadams/[email protected]
1717
env:
1818
GITHUB_TOKEN: ${{ github.token }}
19+
- id: parse
20+
env:
21+
GITHUB_TAG: ${{ steps.get_release.outputs.tag_name }}
22+
run: |
23+
# This pattern comes from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
24+
# However, Bash uses POSIX regular expressions, and there are some unsupported features.
25+
# POSIX does not support non-capturing groups: (?:...)
26+
# - To make it compatible, the non-capture modifiers have been removed
27+
# POSIX does not support the digit metacharacter: \d
28+
# - To make it compatible, the digit metacharacters have been replaced with [0-9]
29+
30+
semantic_version_pattern='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
31+
32+
if [[ ${GITHUB_REF/refs\/tags\//} =~ $semantic_version_pattern ]]; then
33+
echo ::set-output name=is_semantic_version::'true'
34+
else
35+
echo ::set-output name=is_semantic_version::'false'
36+
fi
1937
2038
stable:
39+
needs: tag
40+
if: ${{ needs.tag.outputs.is_semantic_version == 'true' }}
2141
name: Publish to Stable
2242
runs-on: ubuntu-latest
2343
environment: stable
24-
needs: extract
2544
steps:
2645
- name: Checkout Repository
2746
uses: actions/checkout@v2
@@ -43,4 +62,11 @@ jobs:
4362
- name: Pack Projects into Nuget Packages
4463
run: dotnet pack EntityDb.sln --no-restore -c Release /p:Version=${{ needs.extract.outputs.version }}
4564
- name: Publish to Stable
46-
run: dotnet nuget push */**.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
65+
run: dotnet nuget push ./**/*.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
66+
- name: Packages & Symbols Artifact
67+
uses: actions/[email protected]
68+
with:
69+
name: Packages & Symbols
70+
path: |
71+
./**/*.nupkg
72+
./**/*.snupkg

generate-coverage-report.sh

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/EntityDb.Common/Extensions/ServiceCollectionExtensions.cs

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
using EntityDb.Common.Entities;
77
using EntityDb.Common.Projections;
88
using EntityDb.Common.Transactions.Builders;
9-
using EntityDb.Common.Transactions.Processors;
109
using EntityDb.Common.Transactions.Subscribers;
10+
using EntityDb.Common.Transactions.Subscribers.ProcessorQueues;
11+
using EntityDb.Common.Transactions.Subscribers.Processors;
1112
using EntityDb.Common.TypeResolvers;
1213
using Microsoft.Extensions.DependencyInjection;
1314
using System;
1415
using System.Diagnostics.CodeAnalysis;
1516
using System.Reflection;
17+
using System.Threading.Tasks.Dataflow;
1618

1719
namespace EntityDb.Common.Extensions;
1820

@@ -21,34 +23,24 @@ namespace EntityDb.Common.Extensions;
2123
/// </summary>
2224
public static class ServiceCollectionExtensions
2325
{
24-
[ExcludeFromCodeCoverage(Justification = "Don't need coverage for non-test mode.")]
25-
private static void AddSnapshotTransactionProcessor<TTransactionProcessor>(
26-
this IServiceCollection serviceCollection, bool testMode,
27-
Func<IServiceProvider, TTransactionProcessor> transactionProcessorFactory)
26+
private static void AddTestModeTransactionProcessorQueue<TTransactionProcessor>(this IServiceCollection serviceCollection)
2827
where TTransactionProcessor : ITransactionProcessor
2928
{
30-
serviceCollection.AddSingleton(serviceProvider =>
31-
{
32-
var transactionProcessor = transactionProcessorFactory.Invoke(serviceProvider);
29+
serviceCollection.AddSingleton<ITransactionProcessorQueue<TTransactionProcessor>, TestModeTransactionQueue<TTransactionProcessor>>();
30+
}
3331

34-
return TransactionProcessorSubscriber<TTransactionProcessor>.Create(serviceProvider, transactionProcessor,
35-
testMode);
36-
});
32+
[ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")]
33+
private static void AddBufferBlockTransactionProcessorQueue<TTransactionProcessor>(this IServiceCollection serviceCollection)
34+
where TTransactionProcessor : ITransactionProcessor
35+
{
36+
serviceCollection.AddSingleton<BufferBlockTransactionQueue<TTransactionProcessor>>();
3737

38-
serviceCollection.AddSingleton<ITransactionSubscriber>(
39-
serviceProvider =>
40-
serviceProvider.GetRequiredService<TransactionProcessorSubscriber<TTransactionProcessor>>()
41-
);
38+
serviceCollection.AddSingleton<ITransactionProcessorQueue<TTransactionProcessor>>(serviceProvider => serviceProvider.GetRequiredService<BufferBlockTransactionQueue<TTransactionProcessor>>());
4239

43-
if (testMode)
44-
{
45-
return;
46-
}
40+
serviceCollection.AddHostedService(serviceProvider =>
41+
serviceProvider.GetRequiredService<BufferBlockTransactionQueue<TTransactionProcessor>>());
4742

48-
serviceCollection.AddHostedService(
49-
serviceProvider =>
50-
serviceProvider.GetRequiredService<TransactionProcessorSubscriber<TTransactionProcessor>>()
51-
);
43+
serviceCollection.AddSingleton<ITransactionSubscriber, TransactionProcessorSubscriber<TTransactionProcessor>>();
5244
}
5345

5446
internal static void Add<TService>(this IServiceCollection serviceCollection, ServiceLifetime serviceLifetime,
@@ -64,6 +56,39 @@ internal static void Add<TService>(this IServiceCollection serviceCollection, Se
6456
serviceCollection.Add(new ServiceDescriptor(typeof(TService), typeof(TService), serviceLifetime));
6557
}
6658

59+
/// <summary>
60+
/// Registers the transaction processor provided, along with a transaction processor subscriber,
61+
/// and a transaction processor queue. For test mode, the queue is not actually a queue and will
62+
/// immediately process the transaction. For non-test mode, the queue uses a <see cref="BufferBlock{ITransaction}"/>.
63+
/// </summary>
64+
/// <typeparam name="TTransactionProcessor">The type of the transaction processor.</typeparam>
65+
/// <param name="serviceCollection">The service collection.</param>
66+
/// <param name="testMode">Wether or not to run in test mode.</param>
67+
/// <param name="transactionProcessorFactory">A factory for creating the transaction processor.</param>
68+
[ExcludeFromCodeCoverage(Justification = "Tests are only meant to run in test mode.")]
69+
public static void AddTransactionProcessorSubscriber<TTransactionProcessor>(this IServiceCollection serviceCollection,
70+
bool testMode, Func<IServiceProvider, TTransactionProcessor> transactionProcessorFactory)
71+
where TTransactionProcessor : class, ITransactionProcessor
72+
{
73+
serviceCollection.AddSingleton(transactionProcessorFactory.Invoke);
74+
75+
serviceCollection.AddSingleton<TransactionProcessorSubscriber<TTransactionProcessor>>();
76+
77+
serviceCollection.AddSingleton<ITransactionSubscriber>(
78+
serviceProvider =>
79+
serviceProvider.GetRequiredService<TransactionProcessorSubscriber<TTransactionProcessor>>()
80+
);
81+
82+
if (testMode)
83+
{
84+
serviceCollection.AddTestModeTransactionProcessorQueue<TTransactionProcessor>();
85+
}
86+
else
87+
{
88+
serviceCollection.AddBufferBlockTransactionProcessorQueue<TTransactionProcessor>();
89+
}
90+
}
91+
6792
/// <summary>
6893
/// Adds an internal implementation of <see cref="IPartialTypeResolver" /> which resolves types by using assembly
6994
/// information.
@@ -137,7 +162,7 @@ public static void AddEntitySnapshotTransactionSubscriber<TEntity>(this IService
137162
string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false)
138163
where TEntity : IEntity<TEntity>
139164
{
140-
serviceCollection.AddSnapshotTransactionProcessor(testMode, serviceProvider => EntitySnapshotTransactionProcessor<TEntity>.Create(
165+
serviceCollection.AddTransactionProcessorSubscriber(testMode, serviceProvider => EntitySnapshotTransactionProcessor<TEntity>.Create(
141166
serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName));
142167
}
143168

@@ -167,7 +192,7 @@ public static void AddProjectionSnapshotTransactionSubscriber<TProjection>(
167192
string transactionSessionOptionsName, string snapshotSessionOptionsName, bool testMode = false)
168193
where TProjection : IProjection<TProjection>
169194
{
170-
serviceCollection.AddSnapshotTransactionProcessor(testMode, serviceProvider => ProjectionSnapshotTransactionProcessor<TProjection>.Create(
195+
serviceCollection.AddTransactionProcessorSubscriber(testMode, serviceProvider => ProjectionSnapshotTransactionProcessor<TProjection>.Create(
171196
serviceProvider, transactionSessionOptionsName, snapshotSessionOptionsName));
172197
}
173198
}

src/EntityDb.Common/Projections/ProjectionRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public static IProjectionRepository<TProjection> Create(IServiceProvider service
5959
ITransactionRepository transactionRepository,
6060
ISnapshotRepository<TProjection>? snapshotRepository = null)
6161
{
62-
if (snapshotRepository == null)
62+
if (snapshotRepository is null)
6363
{
6464
return ActivatorUtilities.CreateInstance<ProjectionRepository<TProjection>>(serviceProvider,
6565
transactionRepository);

src/EntityDb.Common/Transactions/Processors/ITransactionProcessor.cs

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)