Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
ustims committed Aug 13, 2023
2 parents 8bb28db + b188305 commit 4b15c56
Show file tree
Hide file tree
Showing 39 changed files with 831 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [ "main" ]

env:
PACKAGE_VERSION: 0.1.0
PACKAGE_VERSION: 0.1.4

jobs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static IEventSourcingBuilder AddCosmosDbEventStore(
string connectionString,
CosmosClientOptions cosmosClientOptions,
string databaseId,
string containerId,
string eventsContainerId,
string itemsContainerId,
CosmosClient leaseClient,
string leaseDatabaseId,
string leaseContainerId,
Expand All @@ -28,13 +29,13 @@ string processorName
var cosmosClient = new CosmosClient(connectionString, cosmosClientOptions);
var eventStore = new CosmosDbEventStore(cosmosClient, databaseId, containerId);
var eventStore = new CosmosDbEventStore(cosmosClient, databaseId, eventsContainerId, itemsContainerId);
eventStore.Initialize().Wait();
var eventStoreObserver = new CosmosDbEventStoreChangeFeedObserver(
cosmosClient,
databaseId,
containerId,
eventsContainerId,
leaseClient,
leaseDatabaseId,
leaseContainerId,
Expand All @@ -55,10 +56,11 @@ public static IEventSourcingBuilder AddCosmosDbEventStore(
this IServiceCollection services,
CosmosClient client,
string databaseId,
string containerId
string eventsContainerId,
string itemsContainerId
)
{
var eventStore = new CosmosDbEventStore(client, databaseId, containerId);
var eventStore = new CosmosDbEventStore(client, databaseId, eventsContainerId, itemsContainerId);
eventStore.Initialize().Wait();

return new EventSourcingBuilder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using CloudFabric.EventSourcing.EventStore;
using System.Runtime.CompilerServices;
using CloudFabric.EventSourcing.Domain;
using CloudFabric.EventSourcing.EventStore.InMemory;
using CloudFabric.Projections;
using CloudFabric.Projections.InMemory;
Expand All @@ -12,7 +14,8 @@ public static class ServiceCollectionExtensions
{
public static IEventSourcingBuilder AddInMemoryEventStore(
this IServiceCollection services,
Dictionary<(Guid, string), List<string>> eventsContainer
Dictionary<(Guid, string), List<string>> eventsContainer,
Dictionary<(string, string), string> itemsContainer
)
{
var builder = new EventSourcingBuilder
Expand All @@ -23,7 +26,7 @@ public static IEventSourcingBuilder AddInMemoryEventStore(
services.AddScoped<IEventStore>(
(sp) =>
{
var eventStore = new InMemoryEventStore(eventsContainer);
var eventStore = new InMemoryEventStore(eventsContainer, itemsContainer);
eventStore.Initialize().Wait();
// add events observer for projections
Expand Down Expand Up @@ -58,6 +61,14 @@ public static IEventSourcingBuilder AddInMemoryEventStore(
return builder;
}

public static IEventSourcingBuilder AddInMemoryEventStore(this IServiceCollection services)
{
return services.AddInMemoryEventStore(
new Dictionary<(Guid, string), List<string>>(),
new Dictionary<(string, string), string>()
);
}

public static IEventSourcingBuilder AddRepository<TRepo>(this IEventSourcingBuilder builder)
where TRepo : class
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using CloudFabric.EventSourcing.EventStore;
using CloudFabric.EventSourcing.Domain;
using CloudFabric.EventSourcing.EventStore.Enums;
using CloudFabric.EventSourcing.EventStore.Postgresql;
using CloudFabric.Projections;
using CloudFabric.Projections.Postgresql;
Expand All @@ -20,10 +22,13 @@ public static class ServiceCollectionExtensions
public static IEventSourcingBuilder AddPostgresqlEventStore(
this IServiceCollection services,
string eventsConnectionString,
string tableName
string eventsTableName,
string itemsTableName
)
{
services.AddPostgresqlEventStore((sp) => new PostgresqlEventStoreStaticConnectionInformationProvider(eventsConnectionString, tableName));
services.AddPostgresqlEventStore((sp) =>
new PostgresqlEventStoreStaticConnectionInformationProvider(eventsConnectionString, eventsTableName, itemsTableName)
);

return new EventSourcingBuilder
{
Expand Down Expand Up @@ -103,6 +108,22 @@ Func<IServiceProvider, IPostgresqlEventStoreConnectionInformationProvider> conne
return builder;
}

/// <summary>
/// This extension overload initialize event store with default item event store table name.
/// </summary>
public static IEventSourcingBuilder AddPostgresqlEventStore(
this IServiceCollection services,
string eventsConnectionString,
string eventsTableName
)
{
return services.AddPostgresqlEventStore(
eventsConnectionString,
eventsTableName,
string.Concat(eventsTableName, ItemsEventStoreNameSuffix.TableNameSuffix)
);
}

public static IEventSourcingBuilder AddRepository<TRepo>(this IEventSourcingBuilder builder)
where TRepo : class
{
Expand Down Expand Up @@ -152,7 +173,9 @@ public static IEventSourcingBuilder AddProjectionsRebuildProcessor(this IEventSo
{
var connectionInformationProvider = sp.GetRequiredService<IPostgresqlEventStoreConnectionInformationProvider>();
var connectionInformation = connectionInformationProvider.GetConnectionInformation(connectionId);
var eventStore = new PostgresqlEventStore(connectionInformation.ConnectionString, connectionInformation.TableName);
var eventStore = new PostgresqlEventStore(
connectionInformation.ConnectionString, connectionInformation.TableName, connectionInformation.ItemsTableName
);
var eventObserver = new PostgresqlEventStoreEventObserver(
(PostgresqlEventStore)eventStore,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace CloudFabric.EventSourcing.EventStore.Enums;

public static class ItemsEventStoreNameSuffix
{
public const string TableNameSuffix = "-item";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace CloudFabric.EventSourcing.EventStore;

public static class EventSerializerOptions
public static class EventStoreSerializerOptions
{
public static JsonSerializerOptions Options
{
Expand Down
4 changes: 4 additions & 0 deletions CloudFabric.EventSourcing.EventStore/IEventStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ Task<bool> AppendToStreamAsync(
Task DeleteAll(CancellationToken cancellationToken = default);

Task<bool> HardDeleteAsync(Guid streamId, string partitionKey, CancellationToken cancellationToken = default);

Task UpsertItem<T>(string id, string partitionKey, T item, CancellationToken cancellationToken = default);

Task<T?> LoadItem<T>(string id, string partitionKey, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public IEvent GetEvent()
throw new Exception("Couldn't find event type. Make sure it's in correct namespace.");
}

var e = (IEvent?)EventData.Deserialize(eventType, EventSerializerOptions.Options);
var e = (IEvent?)EventData.Deserialize(eventType, EventStoreSerializerOptions.Options);

if (e == null)
{
Expand Down
15 changes: 15 additions & 0 deletions CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;

namespace CloudFabric.EventSourcing.EventStore.Persistence;

public class ItemWrapper
{
[JsonPropertyName("id")]
public string? Id { get; set; }

Check warning on line 8 in CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs

View check run for this annotation

Codecov / codecov/patch

CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs#L8

Added line #L8 was not covered by tests

[JsonPropertyName("partition_key")]
public string? PartitionKey { get; set; }

Check warning on line 11 in CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs

View check run for this annotation

Codecov / codecov/patch

CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs#L11

Added line #L11 was not covered by tests

[JsonPropertyName("data")]
public string? ItemData { get; set; }

Check warning on line 14 in CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs

View check run for this annotation

Codecov / codecov/patch

CloudFabric.EventSourcing.EventStore/Persistence/ItemWrapper.cs#L14

Added line #L14 was not covered by tests
}
19 changes: 19 additions & 0 deletions CloudFabric.EventSourcing.Tests/Domain/TestItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace CloudFabric.EventSourcing.Tests.Domain;

public class TestItem
{
public Guid Id { get; set; }

public string Name { get; set; }

public Dictionary<string, TestNestedItemClass> Properties { get; set; }
}

public class TestNestedItemClass
{
public Guid Id { get; set; } = Guid.NewGuid();

public string Name { get; set; } = nameof(Name).ToLowerInvariant();

public DateTime date { get; } = DateTime.MinValue;
}
102 changes: 102 additions & 0 deletions CloudFabric.EventSourcing.Tests/ItemTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using CloudFabric.EventSourcing.EventStore;
using CloudFabric.EventSourcing.Tests.Domain;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CloudFabric.EventSourcing.Tests;
public abstract class ItemTests
{
protected abstract Task<IEventStore> GetEventStore();
private IEventStore _eventStore;

[TestInitialize]
public async Task Initialize()
{
_eventStore = await GetEventStore();
}

[TestCleanup]
public async Task Cleanup()
{
await _eventStore.DeleteAll();
}

[TestMethod]
public async Task SaveItem()
{
var item = new TestItem
{
Id = Guid.NewGuid(),
Name = "Item1",
Properties = new Dictionary<string, TestNestedItemClass>
{
{ Guid.NewGuid().ToString(), new TestNestedItemClass() },
{ Guid.NewGuid().ToString(), new TestNestedItemClass() }
}
};

Func<Task> action = async () => await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
await action.Should().NotThrowAsync();
}

[TestMethod]
public async Task LoadItem()
{
var item = new TestItem
{
Id = Guid.NewGuid(),
Name = "Item1",
Properties = new Dictionary<string, TestNestedItemClass>
{
{ Guid.NewGuid().ToString(), new TestNestedItemClass() }
}
};

await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);

var loadedItem = await _eventStore.LoadItem<TestItem>($"{item.Id}{item.Name}", $"{item.Id}{item.Name}");

loadedItem.Id.Should().Be(item.Id);
loadedItem.Name.Should().Be(item.Name);
loadedItem.Properties.Keys.Should().BeEquivalentTo(item.Properties.Keys);
loadedItem.Properties.Values.Should().BeEquivalentTo(item.Properties.Values);
}

[TestMethod]
public async Task LoadItem_NullIfNotFound()
{
var loadedItem = await _eventStore.LoadItem<TestItem>($"{Guid.NewGuid()}", $"{Guid.NewGuid()}");

loadedItem.Should().BeNull();
}

[TestMethod]
public async Task UpdateItem()
{
var item = new TestItem
{
Id = Guid.NewGuid(),
Name = "Item1",
Properties = new Dictionary<string, TestNestedItemClass>
{
{ Guid.NewGuid().ToString(), new TestNestedItemClass() },
{ Guid.NewGuid().ToString(), new TestNestedItemClass() }
}
};

await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);

string propertyGuid = Guid.NewGuid().ToString();

item.Properties = new()
{
{ propertyGuid, new TestNestedItemClass() }
};

Func<Task> action = async () => await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
await action.Should().NotThrowAsync();

var updatedItem = await _eventStore.LoadItem<TestItem>($"{item.Id}{item.Name}", $"{item.Id}{item.Name}");
updatedItem.Properties.ContainsKey(propertyGuid).Should().BeTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ public static class FilterConnectorQueryStringExtensions
{
public static string Serialize(this FilterConnector filterConnector)
{
return $"{filterConnector.Logic}+{filterConnector.Filter.Serialize()}";
return $"{filterConnector.Logic}{ProjectionQueryQueryStringExtensions.FILTER_LOGIC_JOIN_CHARACTER}{filterConnector.Filter.Serialize()}";
}

public static FilterConnector Deserialize(string serialized)
{
var separator = "+";
var separator = $"{ProjectionQueryQueryStringExtensions.FILTER_LOGIC_JOIN_CHARACTER}";

var logicEnd = serialized.IndexOf(separator, StringComparison.Ordinal);

Expand Down
Loading

0 comments on commit 4b15c56

Please sign in to comment.