-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add EventStore Hosting and Client integrations (#277)
* Add EventStore Hosting and Client integrations * Update CODEOWNERS * Address PR feedback * Add client integration tests * Update AddEventStoreTests.cs * Add HealthCheckTimeout setting * Remove HealthCheckTimeout from Conformance tests * Remove Atom Pub over HTTP as support is deprecated * Add Functional tests * Log volume and mount can be added manually * Remove unused const * Update EventStoreFunctionalTests.cs * Update EventStoreFunctionalTests.cs * Update EventStoreFunctionalTests.cs * Wait for resource to be healthy * Update EventStoreFunctionalTests.cs * Update EventStoreFunctionalTests.cs * Fix typo * Use Cts * Update EventStoreFunctionalTests.cs * Check last test * Update EventStoreFunctionalTests.cs * Trying all tests * Update EventStoreFunctionalTests.cs * Update EventStoreFunctionalTests.cs * Update EventStoreFunctionalTests.cs --------- Co-authored-by: Aaron Powell <[email protected]>
- Loading branch information
1 parent
dd34778
commit ea964f7
Showing
40 changed files
with
2,152 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
examples/eventstore/CommunityToolkit.Aspire.Hosting.EventStore.ApiService/Account.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace CommunityToolkit.Aspire.Hosting.EventStore.ApiService; | ||
|
||
public class Account | ||
{ | ||
public Guid Id { get; private set; } | ||
public string? Name { get; private set; } | ||
public decimal Balance { get; private set; } | ||
|
||
[JsonIgnore] | ||
public int Version { get; private set; } = -1; | ||
|
||
[NonSerialized] | ||
private readonly Queue<object> uncommittedEvents = new(); | ||
|
||
public static Account Create(Guid id, string name) | ||
=> new(id, name); | ||
|
||
public void Deposit(decimal amount) | ||
{ | ||
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(amount, 0, nameof(amount)); | ||
|
||
var @event = new AccountFundsDeposited(Id, amount); | ||
|
||
uncommittedEvents.Enqueue(@event); | ||
Apply(@event); | ||
} | ||
|
||
public void Withdraw(decimal amount) | ||
{ | ||
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(amount, 0, nameof(amount)); | ||
ArgumentOutOfRangeException.ThrowIfGreaterThan(amount, Balance, nameof(amount)); | ||
|
||
var @event = new AccountFundsWithdrew(Id, amount); | ||
|
||
uncommittedEvents.Enqueue(@event); | ||
Apply(@event); | ||
} | ||
|
||
public void When(object @event) | ||
{ | ||
switch (@event) | ||
{ | ||
case AccountCreated accountCreated: | ||
Apply(accountCreated); | ||
break; | ||
case AccountFundsDeposited accountFundsDeposited: | ||
Apply(accountFundsDeposited); | ||
break; | ||
case AccountFundsWithdrew accountFundsWithdrew: | ||
Apply(accountFundsWithdrew); | ||
break; | ||
} | ||
} | ||
|
||
public object[] DequeueUncommittedEvents() | ||
{ | ||
var dequeuedEvents = uncommittedEvents.ToArray(); | ||
|
||
uncommittedEvents.Clear(); | ||
|
||
return dequeuedEvents; | ||
} | ||
|
||
private Account() | ||
{ | ||
} | ||
|
||
private Account(Guid id, string name) | ||
{ | ||
if (id == Guid.Empty) | ||
{ | ||
throw new ArgumentException("Id cannot be empty.", nameof(id)); | ||
} | ||
ArgumentException.ThrowIfNullOrWhiteSpace(name, nameof(name)); | ||
|
||
var @event = new AccountCreated(id, name); | ||
|
||
uncommittedEvents.Enqueue(@event); | ||
Apply(@event); | ||
} | ||
|
||
private void Apply(AccountCreated @event) | ||
{ | ||
Version++; | ||
|
||
Id = @event.Id; | ||
Name = @event.Name; | ||
} | ||
|
||
private void Apply(AccountFundsDeposited @event) | ||
{ | ||
Version++; | ||
|
||
Balance += @event.Amount; | ||
} | ||
|
||
private void Apply(AccountFundsWithdrew @event) | ||
{ | ||
Version++; | ||
|
||
Balance -= @event.Amount; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
examples/eventstore/CommunityToolkit.Aspire.Hosting.EventStore.ApiService/AccountEvents.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace CommunityToolkit.Aspire.Hosting.EventStore.ApiService; | ||
|
||
public record AccountCreated(Guid Id, string Name); | ||
|
||
public record AccountFundsDeposited(Guid Id, decimal Amount); | ||
|
||
public record AccountFundsWithdrew(Guid Id, decimal Amount); |
18 changes: 18 additions & 0 deletions
18
...osting.EventStore.ApiService/CommunityToolkit.Aspire.Hosting.EventStore.ApiService.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<Nullable>enable</Nullable> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" /> | ||
<PackageReference Include="Swashbuckle.AspNetCore" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.EventStore\CommunityToolkit.Aspire.EventStore.csproj" /> | ||
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.EventStore.ServiceDefaults\CommunityToolkit.Aspire.Hosting.EventStore.ServiceDefaults.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
76 changes: 76 additions & 0 deletions
76
.../eventstore/CommunityToolkit.Aspire.Hosting.EventStore.ApiService/EventStoreExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using EventStore.Client; | ||
using System.Text.Json; | ||
using System.Text; | ||
|
||
namespace CommunityToolkit.Aspire.Hosting.EventStore.ApiService; | ||
|
||
public static class EventStoreExtensions | ||
{ | ||
public static async Task<Account?> GetAccount(this EventStoreClient eventStore, Guid id, CancellationToken cancellationToken) | ||
{ | ||
var readResult = eventStore.ReadStreamAsync( | ||
Direction.Forwards, | ||
$"account-{id:N}", | ||
StreamPosition.Start, | ||
cancellationToken: cancellationToken | ||
); | ||
|
||
var readState = await readResult.ReadState; | ||
if (readState == ReadState.StreamNotFound) | ||
{ | ||
return null; | ||
} | ||
|
||
var account = (Account)Activator.CreateInstance(typeof(Account), true)!; | ||
|
||
await foreach (var resolvedEvent in readResult) | ||
{ | ||
var @event = resolvedEvent.Deserialize(); | ||
|
||
account.When(@event!); | ||
} | ||
|
||
return account; | ||
} | ||
|
||
public static async Task AppendAcountEvents(this EventStoreClient eventStore, Account account, CancellationToken cancellationToken) | ||
{ | ||
var events = account.DequeueUncommittedEvents(); | ||
|
||
var eventsToAppend = events | ||
.Select(@event => @event.Serialize()).ToArray(); | ||
|
||
var expectedVersion = account.Version - events.Length; | ||
await eventStore.AppendToStreamAsync( | ||
$"account-{account.Id:N}", | ||
expectedVersion == 0 ? StreamRevision.None : StreamRevision.FromInt64(expectedVersion), | ||
eventsToAppend, | ||
cancellationToken: cancellationToken | ||
); | ||
} | ||
|
||
private static object? Deserialize(this ResolvedEvent resolvedEvent) | ||
{ | ||
var eventClrTypeName = JsonDocument.Parse(resolvedEvent.Event.Metadata) | ||
.RootElement | ||
.GetProperty("EventClrTypeName") | ||
.GetString(); | ||
|
||
return JsonSerializer.Deserialize( | ||
Encoding.UTF8.GetString(resolvedEvent.Event.Data.Span), | ||
Type.GetType(eventClrTypeName!)!); | ||
} | ||
|
||
private static EventData Serialize(this object @event) | ||
{ | ||
return new EventData( | ||
Uuid.NewUuid(), | ||
@event.GetType().Name, | ||
data: Encoding.UTF8.GetBytes(JsonSerializer.Serialize(@event)), | ||
metadata: Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new Dictionary<string, string> | ||
{ | ||
{ "EventClrTypeName", @event.GetType().AssemblyQualifiedName! } | ||
})) | ||
); | ||
} | ||
} |
Oops, something went wrong.