Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event item refactor & Acceptance Tests #248

Merged
merged 5 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/build-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ jobs:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
run: dotnet run

- name: Test Cosmos Event Sourcing
env:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
run: |
dotnet test --no-restore --verbosity normal --filter "Category=Acceptance&Type=CosmosEventSourcing"

- name: Prune databases
working-directory: ./tools/CosmosTestHelper
env:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
run: dotnet run

- name: Test Container Creation
env:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/es_docs_deploy.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name: ES Docs Build and Deploy
on:
push:
branches:
- main
branches: [ main ]
paths:
- './docs/event-sourcing/**'
- 'docs/**'

workflow_dispatch:
inputs:
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/nuget-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ jobs:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
run: dotnet run

- name: Test Cosmos Event Sourcing
env:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
run: |
dotnet test --no-restore --verbosity normal --filter "Category=Acceptance&Type=CosmosEventSourcing"

- name: Prune databases
working-directory: ./tools/CosmosTestHelper
env:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
run: dotnet run

- name: Test Container Creation
env:
CosmosConnectionString: ${{ secrets.COSMOS_INTEGRATION_TEST_CONNECTION_STRING }}
Expand Down
8 changes: 8 additions & 0 deletions Microsoft.Azure.CosmosRepository.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\pr-validation.yml = .github\workflows\pr-validation.yml
README.md = README.md
.github\workflows\nuget-publish.yml = .github\workflows\nuget-publish.yml
.github\workflows\es_docs_deploy.yaml = .github\workflows\es_docs_deploy.yaml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceTier", "samples\Microsoft.Azure.CosmosRepository\ServiceTier\ServiceTier.csproj", "{AE98C44F-4F35-4AF8-9141-1DBCB00468C8}"
Expand Down Expand Up @@ -79,6 +80,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventSourcingJobsTracker",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventSourcingCustomerAccount", "samples\Microsoft.Azure.CosmosEventSourcing\EventSourcingCustomerAccount\EventSourcingCustomerAccount.csproj", "{7A2873BC-70A1-489B-9609-E9D7D382C826}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.CosmosEventSourcingAcceptanceTests", "tests\Microsoft.Azure.CosmosEventSourcingAcceptanceTests\Microsoft.Azure.CosmosEventSourcingAcceptanceTests.csproj", "{99ADC0F0-9433-49AD-86FA-34009CD293BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -173,6 +176,10 @@ Global
{7A2873BC-70A1-489B-9609-E9D7D382C826}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A2873BC-70A1-489B-9609-E9D7D382C826}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A2873BC-70A1-489B-9609-E9D7D382C826}.Release|Any CPU.Build.0 = Release|Any CPU
{99ADC0F0-9433-49AD-86FA-34009CD293BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99ADC0F0-9433-49AD-86FA-34009CD293BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99ADC0F0-9433-49AD-86FA-34009CD293BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99ADC0F0-9433-49AD-86FA-34009CD293BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -202,6 +209,7 @@ Global
{FEB072AC-B573-48BF-948D-B5FFF9102303} = {F8ED6752-5ED3-4EA1-89F0-363C40F8D8E0}
{D276C752-4DB7-4380-AF76-91492E8F8554} = {8F8738AD-EBC3-4C24-882B-5D9FAC427E80}
{7A2873BC-70A1-489B-9609-E9D7D382C826} = {8F8738AD-EBC3-4C24-882B-5D9FAC427E80}
{99ADC0F0-9433-49AD-86FA-34009CD293BA} = {F8ED6752-5ED3-4EA1-89F0-363C40F8D8E0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6AAE7641-B62C-48BA-8FE6-0F819E5B45EF}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,22 @@ Once we have defined an `EventItem` then we can configure the library. This stag

## Defining an Event Item

In order to define an `EventItem` you need to define a class that implements `EventItem`. The library offers a type that implements this and supports storing `DomainEvent`'s. This class `DefaultEventItem` is used below.
In order to define an `EventItem` you need to define a class that implements `EventItem`. The library offers a type that implements this and supports storing `DomainEvent`'s. This class `EventItem` is used below.

```csharp
public class CustomerAccountEventItem : DefaultEventItem
public class CustomerAccountEventItem : EventItem
{
public CustomerAccountEventItem(
string username,
IDomainEvent domainEvent)
: base(
eventPayload: domainEvent,
partitionKey: username)
{
}

[JsonConstructor]
public CustomerAccountEventItem(
IDomainEvent eventPayload,
string partitionKey) :
base(eventPayload, partitionKey)
DomainEvent domainEvent)
{
DomainEvent = domainEvent;
PartitionKey = username;
}
}
```

This class dictates that when this object is created it must be provided an `IDomainEvent` and a value to use to partition this `EventItem`.

::: warning Notice
The private constructor defined in the above example is noteworthy. This is required with the `[JsonConstructor]` attribute. The names of the parameters are also _very_ important to ensure de-serialization works correctly.
:::
This class dictates that when this object is created it must be provided a `DomainEvent` and a value to use to partition this `EventItem`.

## Startup Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ app.MapPut(

CustomerAccount account = CustomerAccount.Replay(
eventsItems.Select(x =>
x.DomainEventPayload).ToList());
x.DomainEvent).ToList());
});
```

Expand Down Expand Up @@ -115,7 +115,7 @@ app.MapPut(

CustomerAccount account = CustomerAccount.Replay(
eventsItems.Select(x =>
x.DomainEventPayload).ToList());
x.DomainEvent).ToList());

account.AssignAddress(
request.AddressLine1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public class CustomerAccountReadProjectionBuilder :
CustomerAccountEventItem sourcedEvent,
CancellationToken cancellationToken = default)
{
switch (sourcedEvent.DomainEventPayload)
switch (sourcedEvent.DomainEvent)
{
case CustomerAccountCreated created:
await CreateProjection(created);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@

using Microsoft.Azure.CosmosEventSourcing.Events;
using Microsoft.Azure.CosmosEventSourcing.Items;
using Newtonsoft.Json;

namespace BasicEventSourcingSample.Infrastructure;

public class ShipEventItem : DefaultEventItem
public class ShipEventItem : EventItem
{
public ShipEventItem(
IDomainEvent eventPayload,
DomainEvent domainEvent,
string partitionKey)
: base(eventPayload, partitionKey)
{
DomainEvent = domainEvent;
PartitionKey = partitionKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async ValueTask<Ship> FindAsync(string shipName)
IEnumerable<ShipEventItem> sourcedEvents = await _store.ReadAsync(shipName);

Ship ship = Ship.Build(sourcedEvents.Select(x =>
x.DomainEventPayload).ToList());
x.DomainEvent).ToList());

return ship;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,13 @@

namespace EventSourcingCustomerAccount.Items;

public class CustomerAccountEventItem : DefaultEventItem
public class CustomerAccountEventItem : EventItem
{
public CustomerAccountEventItem(
string username,
IDomainEvent domainEvent)
: base(
eventPayload: domainEvent,
partitionKey: username)
{
}

[JsonConstructor]
public CustomerAccountEventItem(
IDomainEvent eventPayload,
string partitionKey) :
base(eventPayload, partitionKey)
DomainEvent domainEvent)
{
DomainEvent = domainEvent;
PartitionKey = username;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ await eventStore.PersistAsync(

CustomerAccount account = CustomerAccount.Replay(
eventsItems.Select(x =>
x.DomainEventPayload).ToList());
x.DomainEvent).ToList());

account.AssignAddress(
request.AddressLine1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ public CustomerAccountReadProjectionBuilder(
_repository = repository;

public async ValueTask ProjectAsync(
CustomerAccountEventItem sourcedEvent,
CustomerAccountEventItem eventItem,
CancellationToken cancellationToken = default)
{
switch (sourcedEvent.DomainEventPayload)
switch (eventItem.DomainEvent)
{
case CustomerAccountCreated created:
await CreateProjection(created);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@

namespace EventSourcingJobsTracker.Infrastructure.Items;

public class JobsListEventItem : DefaultEventItem
public class JobsListEventItem : EventItem
{
public JobsListEventItem(
IDomainEvent domainEvent,
Guid id) :
base(domainEvent, id.ToString())
{
DomainEvent domainDomainEvent,
Guid id)

{
PartitionKey = id.ToString();
DomainEvent = domainDomainEvent;
}

[JsonConstructor]
private JobsListEventItem(
IDomainEvent eventPayload,
string partitionKey) : base(eventPayload, partitionKey)
private JobsListEventItem()
{

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public JobsListJobAddedProjection(IWriteOnlyRepository<JobItem> repository) =>

public async ValueTask HandleAsync(
JobAddedEvent domainEvent,
JobsListEventItem eventSource,
JobsListEventItem eventItem,
CancellationToken cancellationToken = default)
{
(Guid guid, string? title, DateTime due, JobListInfo? jobListInfo) = domainEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public JobsListJobCompletedProjection(IRepository<JobItem> repository)

public async ValueTask HandleAsync(
JobCompletedEvent domainEvent,
JobsListEventItem eventSource,
JobsListEventItem eventItem,
CancellationToken cancellationToken = default)
{
(Guid id, string? _, JobListInfo? jobListInfo) = domainEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public UsersJobsListProjection(IWriteOnlyRepository<JobsListReadItem> repository

public async ValueTask HandleAsync(
JobListCreatedEvent domainEvent,
JobsListEventItem eventSource,
JobsListEventItem eventItem,
CancellationToken cancellationToken = default)
{
(Guid guid, string? name, string? category, string? username) = domainEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ public async ValueTask<JobsList> ReadAsync(Guid jobListId)
$"There is no job list with the ID {jobListId}");
}

return JobsList.Replay(events.Select(x => x.DomainEventPayload).ToList());
return JobsList.Replay(events.Select(x => x.DomainEvent).ToList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ protected void Apply(List<DomainEvent> domainEvents)
/// <![CDATA[
/// public class MyAggregate : AggregateRoot
/// {
/// protected override void Apply(DomainEvent domainEvent)
/// protected override void Apply(DomainEvent domainDomainEvent)
/// {
/// switch (domainEvent)
/// switch (domainDomainEvent)
/// {
/// case ShipEvents.ShipCreated created:
/// Apply(created);
Expand All @@ -110,8 +110,8 @@ protected void Apply(List<DomainEvent> domainEvents)
/// break;
/// default:
/// throw new ArgumentOutOfRangeException(
/// nameof(domainEvent),
/// $"No apply method found for {domainEvent.GetType().Name}");
/// nameof(domainDomainEvent),
/// $"No apply method found for {domainDomainEvent.GetType().Name}");
/// }
/// }
/// }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public AtomicEvent(Guid id, string eTag)
ETag = eTag;
}

[JsonProperty("id")]
internal Guid Id { get; init; }

internal string ETag { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.Azure.CosmosEventSourcing.Extensions;
/// <summary>
/// A set of extension methods that can be used on an <see cref="EventItem"/>
/// </summary>
public static class EventSourceExtensions
public static class EventItemExtensions
{
/// <summary>
/// Cast's the payload of an <see cref="EventItem"/> to a <see cref="IDomainEvent"/>
Expand All @@ -18,8 +18,8 @@ public static class EventSourceExtensions
/// <typeparam name="TEvent">The event type the payload will be cast to.</typeparam>
/// <returns>The TEvent instance.</returns>
public static TEvent GetEventPayload<TEvent>(this EventItem eventItem)
where TEvent : IDomainEvent =>
(TEvent) eventItem.EventPayload;
where TEvent : DomainEvent =>
(TEvent) eventItem.DomainEvent;

/// <summary>
/// Cast's the payload of an <see cref="EventItem"/> to a <see cref="IDomainEvent"/>
Expand All @@ -29,8 +29,8 @@ public static TEvent GetEventPayload<TEvent>(this EventItem eventItem)
/// <returns>The TEvent instance.</returns>
/// <remarks>If the event payload cannot be converted to the TEvent type null is returned.</remarks>
public static TEvent? TryGetEventPayload<TEvent>(this EventItem eventItem)
where TEvent : class, IDomainEvent =>
eventItem.EventPayload switch
where TEvent : DomainEvent =>
eventItem.DomainEvent switch
{
TEvent eventPayload => eventPayload,
null => null,
Expand Down
Loading