diff --git a/Source/Kernel/Events.Projections/Changes/Change.cs b/Source/Fundamentals/Changes/Change.cs similarity index 75% rename from Source/Kernel/Events.Projections/Changes/Change.cs rename to Source/Fundamentals/Changes/Change.cs index 117a00e20..2e7f8679b 100644 --- a/Source/Kernel/Events.Projections/Changes/Change.cs +++ b/Source/Fundamentals/Changes/Change.cs @@ -3,10 +3,10 @@ using System.Dynamic; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// - /// Defines a change as part of a . + /// Defines a change as part of a . /// /// State after change applied. public record Change(ExpandoObject State); diff --git a/Source/Kernel/Events.Projections/Changes/Changes.cs b/Source/Fundamentals/Changes/Changes.cs similarity index 63% rename from Source/Kernel/Events.Projections/Changes/Changes.cs rename to Source/Fundamentals/Changes/Changes.cs index d98d5d877..8ee0bb275 100644 --- a/Source/Kernel/Events.Projections/Changes/Changes.cs +++ b/Source/Fundamentals/Changes/Changes.cs @@ -3,28 +3,30 @@ using System.Dynamic; using Cratis.Dynamic; +using Cratis.Properties; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// - /// Represents changes that can be applied to a . + /// Represents changes that can be applied to a . /// public static class Changes { /// - /// Applies properties to the . + /// Applies properties to the . /// - /// to apply to. - /// Collection of property mappers that will manipulate properties on the target. + /// Type the changeset is for. + /// to apply to. + /// Collection of property mappers that will manipulate properties on the target. /// /// This will run a diff against the initial state and only produce changes that are new. /// - public static void ApplyProperties(this Changeset changeset, IEnumerable propertyMappers) + public static void ApplyProperties(this Changeset changeset, IEnumerable> propertyMappers) { var workingState = changeset.InitialState.Clone(); foreach (var propertyMapper in propertyMappers) { - propertyMapper(changeset.Event, workingState); + propertyMapper(changeset.Incoming, workingState); } var comparer = new ObjectsComparer.Comparer(); @@ -35,29 +37,30 @@ public static void ApplyProperties(this Changeset changeset, IEnumerable - /// Applies properties for a child to the . + /// Applies properties for a child to the . /// - /// to apply to. + /// Type the changeset is for. + /// to apply to. /// The item to add from. /// The on the parent that holds the children. /// The on the instance that identifies the child. - /// The for resolving the key on the event. - /// Collection of property mappers that will manipulate properties on the target. + /// The for resolving the key on the event. + /// Collection of property mappers that will manipulate properties on the target. /// /// This will run a diff against the initial state and only produce changes that are new. /// - public static void ApplyChildProperties( - this Changeset changeset, + public static void ApplyChildProperties( + this Changeset changeset, ExpandoObject item, Property childrenProperty, Property identifiedByProperty, - EventValueProvider keyResolver, - IEnumerable propertyMappers) + ValueProvider keyResolver, + IEnumerable> propertyMappers) { var workingItem = item.Clone(); foreach (var propertyMapper in propertyMappers) { - propertyMapper(changeset.Event, workingItem); + propertyMapper(changeset.Incoming, workingItem); } var comparer = new ObjectsComparer.Comparer(); @@ -67,26 +70,27 @@ public static void ApplyChildProperties( workingItem, childrenProperty, identifiedByProperty, - keyResolver(changeset.Event), + keyResolver(changeset.Incoming), differences.Select(_ => new PropertyDifference(item, workingItem, _)))); } } /// - /// Applies properties to the child in the model to the . + /// Applies properties to the child in the model to the . /// - /// to apply to. + /// Type the changeset is for. + /// to apply to. /// for accessing the children collection. /// that identifies the child. /// Key value. - /// Collection of property mappers that will manipulate properties on the target. + /// Collection of property mappers that will manipulate properties on the target. /// Thrown when children property is not enumerable. - public static void ApplyAddChild( - this Changeset changeset, + public static void ApplyAddChild( + this Changeset changeset, Property childrenProperty, Property identifiedByProperty, object key, - IEnumerable propertyMappers) + IEnumerable> propertyMappers) { var workingState = changeset.InitialState.Clone(); var items = workingState.EnsureCollection(childrenProperty); @@ -97,7 +101,7 @@ public static void ApplyAddChild( foreach (var propertyMapper in propertyMappers) { - propertyMapper(changeset.Event, item); + propertyMapper(changeset.Incoming, item); } identifiedByProperty.SetValue(item, key); @@ -108,18 +112,20 @@ public static void ApplyAddChild( } /// - /// Apply a remove change to the . + /// Apply a remove change to the . /// - /// to apply to. - public static void ApplyRemove(this Changeset changeset) + /// Type the changeset is for. + /// to apply to. + public static void ApplyRemove(this Changeset changeset) { } /// - /// Apply a remove child change to the . + /// Apply a remove child change to the . /// - /// to apply to. - public static void ApplyRemoveChild(this Changeset changeset) + /// Type the changeset is for. + /// to apply to. + public static void ApplyRemoveChild(this Changeset changeset) { } } diff --git a/Source/Kernel/Events.Projections/Changes/Changeset.cs b/Source/Fundamentals/Changes/Changeset.cs similarity index 64% rename from Source/Kernel/Events.Projections/Changes/Changeset.cs rename to Source/Fundamentals/Changes/Changeset.cs index 3b5af675c..d77633f18 100644 --- a/Source/Kernel/Events.Projections/Changes/Changeset.cs +++ b/Source/Fundamentals/Changes/Changeset.cs @@ -3,30 +3,31 @@ using System.Dynamic; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// - /// Represents a changeset - the consequence of an individual handling of a . + /// Represents a changeset of changes that can occur to an object. /// - public class Changeset + /// Type of object we're working on. + public class Changeset { readonly List _changes = new(); /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - /// that the is for. + /// that the is for. /// The initial state before any changes are applied. - public Changeset(Event @event, ExpandoObject initialState) + public Changeset(T incoming, ExpandoObject initialState) { - Event = @event; + Incoming = incoming; InitialState = initialState; } /// - /// Gets the the is for. + /// Gets the the is for. /// - public Event Event { get; } + public T Incoming { get; } /// /// Gets the initial state of before changes in changeset occurred. diff --git a/Source/Kernel/Events.Projections/Changes/ChangesetExtensions.cs b/Source/Fundamentals/Changes/ChangesetExtensions.cs similarity index 66% rename from Source/Kernel/Events.Projections/Changes/ChangesetExtensions.cs rename to Source/Fundamentals/Changes/ChangesetExtensions.cs index 6a405cc04..1ccaf6844 100644 --- a/Source/Kernel/Events.Projections/Changes/ChangesetExtensions.cs +++ b/Source/Fundamentals/Changes/ChangesetExtensions.cs @@ -2,22 +2,25 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Dynamic; +using Cratis.Properties; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// - /// Extension methods for working with . + /// Extension methods for working with . /// public static class ChangesetExtensions { /// /// Check if changeset contains a to a collection with a specific key. /// - /// to check. + /// Type of object for the changeset. + /// to check. /// The representing the collection. /// The key of the item. /// True if it has, false it not. - public static bool HasChildBeenAddedWithKey(this Changeset changeset, Property childrenProperty, object key) + public static bool HasChildBeenAddedWithKey(this Changeset changeset, Property childrenProperty, object key) { return changeset.Changes .Select(_ => _ as ChildAdded) @@ -27,12 +30,13 @@ public static bool HasChildBeenAddedWithKey(this Changeset changeset, Property c /// /// Get a specific child from /// - /// to get from. + /// Type of object for the changeset. + /// to get from. /// The representing the collection. /// The that identifies the child /// The key of the item. /// The added child. - public static ExpandoObject GetChildByKey(this Changeset changeset, Property childrenProperty, Property identifiedByProperty, object key) + public static ExpandoObject GetChildByKey(this Changeset changeset, Property childrenProperty, Property identifiedByProperty, object key) { var items = childrenProperty.GetValue(changeset.InitialState) as IEnumerable; return items!.FindByKey(identifiedByProperty, key)!; diff --git a/Source/Kernel/Events.Projections/Changes/ChildAdded.cs b/Source/Fundamentals/Changes/ChildAdded.cs similarity index 93% rename from Source/Kernel/Events.Projections/Changes/ChildAdded.cs rename to Source/Fundamentals/Changes/ChildAdded.cs index 6abcec538..e627e978c 100644 --- a/Source/Kernel/Events.Projections/Changes/ChildAdded.cs +++ b/Source/Fundamentals/Changes/ChildAdded.cs @@ -2,8 +2,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// /// Represents a child that has been added to a parent. diff --git a/Source/Kernel/Events.Projections/Changes/ChildPropertiesChanged.cs b/Source/Fundamentals/Changes/ChildPropertiesChanged.cs similarity index 94% rename from Source/Kernel/Events.Projections/Changes/ChildPropertiesChanged.cs rename to Source/Fundamentals/Changes/ChildPropertiesChanged.cs index cfbca39f6..83df5deba 100644 --- a/Source/Kernel/Events.Projections/Changes/ChildPropertiesChanged.cs +++ b/Source/Fundamentals/Changes/ChildPropertiesChanged.cs @@ -2,8 +2,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// /// Represents properties that has been changed on a child. diff --git a/Source/Kernel/Events.Projections/Changes/PropertiesChanged.cs b/Source/Fundamentals/Changes/PropertiesChanged.cs similarity index 92% rename from Source/Kernel/Events.Projections/Changes/PropertiesChanged.cs rename to Source/Fundamentals/Changes/PropertiesChanged.cs index 5dadaa3f7..b50043fde 100644 --- a/Source/Kernel/Events.Projections/Changes/PropertiesChanged.cs +++ b/Source/Fundamentals/Changes/PropertiesChanged.cs @@ -3,7 +3,7 @@ using System.Dynamic; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// /// Represents properties that has been changed. diff --git a/Source/Kernel/Events.Projections/Changes/PropertyDifference.cs b/Source/Fundamentals/Changes/PropertyDifference.cs similarity index 98% rename from Source/Kernel/Events.Projections/Changes/PropertyDifference.cs rename to Source/Fundamentals/Changes/PropertyDifference.cs index d38aa17f4..71bed71d3 100644 --- a/Source/Kernel/Events.Projections/Changes/PropertyDifference.cs +++ b/Source/Fundamentals/Changes/PropertyDifference.cs @@ -6,7 +6,7 @@ using System.Reflection; using ObjectsComparer; -namespace Cratis.Events.Projections.Changes +namespace Cratis.Changes { /// /// Represents a value difference in a property of an object. diff --git a/Source/Kernel/Events.Projections/RemovePerformed.cs b/Source/Fundamentals/Changes/RemovePerformed.cs similarity index 82% rename from Source/Kernel/Events.Projections/RemovePerformed.cs rename to Source/Fundamentals/Changes/RemovePerformed.cs index 564e2ec25..f69c58b03 100644 --- a/Source/Kernel/Events.Projections/RemovePerformed.cs +++ b/Source/Fundamentals/Changes/RemovePerformed.cs @@ -2,9 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; -namespace Cratis.Events.Projections +namespace Cratis.Changes { /// /// Represents an entry being removed. diff --git a/Source/Fundamentals/Dynamic/ExpandoObjectExtensions.cs b/Source/Fundamentals/Dynamic/ExpandoObjectExtensions.cs index 3c73954e5..98057ed12 100644 --- a/Source/Fundamentals/Dynamic/ExpandoObjectExtensions.cs +++ b/Source/Fundamentals/Dynamic/ExpandoObjectExtensions.cs @@ -1,8 +1,10 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Collections; using System.Dynamic; using Cratis.Concepts; +using Cratis.Properties; namespace Cratis.Dynamic { @@ -95,5 +97,81 @@ public static ExpandoObject OverwriteWith(this ExpandoObject left, ExpandoObject return result; } + + /// + /// Ensure a specific path for a exists on an .. + /// + /// Target . + /// to get or create for. + /// at property. + public static ExpandoObject EnsurePath(this ExpandoObject target, Property property) + { + var currentTarget = target as IDictionary; + for (var propertyIndex = 0; propertyIndex < property.Segments.Length - 1; propertyIndex++) + { + var segment = property.Segments[propertyIndex]; + if (!currentTarget.ContainsKey(segment)) + { + var nested = new ExpandoObject(); + currentTarget[segment] = nested; + currentTarget = nested!; + } + else + { + currentTarget = ((ExpandoObject)currentTarget[segment])!; + } + } + + return (currentTarget as ExpandoObject)!; + } + + /// + /// Ensures that a collection exists for a specific . + /// + /// Target . + /// to ensure collection for. + /// The ensured . + /// Thrown if there is an existing property and it is not enumerable. + public static ICollection EnsureCollection(this ExpandoObject target, Property childrenProperty) + { + var inner = target.EnsurePath(childrenProperty) as IDictionary; + if (!inner.ContainsKey(childrenProperty.LastSegment)) + { + inner[childrenProperty.LastSegment] = new List(); + } + + if (!(inner[childrenProperty.LastSegment] is IEnumerable)) + { + throw new ChildrenPropertyIsNotEnumerable(childrenProperty); + } + + var items = (inner[childrenProperty.LastSegment] as IEnumerable)!.Cast(); + if (items is not IList) + { + items = new List(items!); + } + inner[childrenProperty.LastSegment] = items; + return (items as ICollection)!; + } + + /// + /// Check if there is an item with a specific key in a collection of items. + /// + /// Items to check. + /// holding identity on each item. + /// The key value to check for. + /// True if there is an item, false if not + public static bool Contains(this IEnumerable items, Property identityProperty, object key) => + items!.Any((IDictionary _) => _.ContainsKey(identityProperty.Path) && _[identityProperty.Path].Equals(key)); + + /// + /// Find an item in a collection by its identity. + /// + /// Items to find from. + /// holding identity on each item. + /// The key value to check for. + /// The item or default if not found. + public static ExpandoObject? FindByKey(this IEnumerable items, Property identityProperty, object key) => + items!.FirstOrDefault((IDictionary _) => _.ContainsKey(identityProperty.Path) && _[identityProperty.Path].Equals(key)) as ExpandoObject; } } diff --git a/Source/Fundamentals/Fundamentals.csproj b/Source/Fundamentals/Fundamentals.csproj index 22dc8d0ea..997215033 100644 --- a/Source/Fundamentals/Fundamentals.csproj +++ b/Source/Fundamentals/Fundamentals.csproj @@ -11,6 +11,7 @@ + diff --git a/Source/Kernel/Events.Projections/ChildrenPropertyIsNotEnumerable.cs b/Source/Fundamentals/Properties/ChildrenPropertyIsNotEnumerable.cs similarity index 95% rename from Source/Kernel/Events.Projections/ChildrenPropertyIsNotEnumerable.cs rename to Source/Fundamentals/Properties/ChildrenPropertyIsNotEnumerable.cs index 1b57bd798..32a648b53 100644 --- a/Source/Kernel/Events.Projections/ChildrenPropertyIsNotEnumerable.cs +++ b/Source/Fundamentals/Properties/ChildrenPropertyIsNotEnumerable.cs @@ -1,7 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Cratis.Events.Projections +namespace Cratis.Properties { /// /// Exception that is thrown when a children property is not enumerable. diff --git a/Source/Kernel/Events.Projections/Property.cs b/Source/Fundamentals/Properties/Property.cs similarity index 98% rename from Source/Kernel/Events.Projections/Property.cs rename to Source/Fundamentals/Properties/Property.cs index 432f7a9c1..9ffd554ea 100644 --- a/Source/Kernel/Events.Projections/Property.cs +++ b/Source/Fundamentals/Properties/Property.cs @@ -2,9 +2,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Dynamic; using Cratis.Strings; -namespace Cratis.Events.Projections +namespace Cratis.Properties { /// /// Represents an encapsulation of a property in the system - used for accessing properties on objects. diff --git a/Source/Kernel/Events.Projections/Json/PropertyJsonConverter.cs b/Source/Fundamentals/Properties/PropertyJsonConverter.cs similarity index 95% rename from Source/Kernel/Events.Projections/Json/PropertyJsonConverter.cs rename to Source/Fundamentals/Properties/PropertyJsonConverter.cs index c23a3fa24..8822486ea 100644 --- a/Source/Kernel/Events.Projections/Json/PropertyJsonConverter.cs +++ b/Source/Fundamentals/Properties/PropertyJsonConverter.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace Cratis.Events.Projections.Json +namespace Cratis.Properties { /// /// Represents a for converting . diff --git a/Source/Fundamentals/Properties/PropertyMapper.cs b/Source/Fundamentals/Properties/PropertyMapper.cs new file mode 100644 index 000000000..088b53f16 --- /dev/null +++ b/Source/Fundamentals/Properties/PropertyMapper.cs @@ -0,0 +1,15 @@ +// Copyright (c) Cratis. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Dynamic; + +namespace Cratis.Properties +{ + /// + /// Represents the delegate of an operation that maps data from a source into a target object. + /// + /// Type of the source. + /// Source object. + /// target to write to. + public delegate void PropertyMapper(T source, ExpandoObject target); +} diff --git a/Source/Fundamentals/Properties/ValueProvider.cs b/Source/Fundamentals/Properties/ValueProvider.cs new file mode 100644 index 000000000..96649bdb0 --- /dev/null +++ b/Source/Fundamentals/Properties/ValueProvider.cs @@ -0,0 +1,13 @@ +// Copyright (c) Cratis. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Cratis.Properties +{ + /// + /// Represents the delegate for providing a value from an object. + /// + /// + /// Source to get from. + /// Resolved value. + public delegate object ValueProvider(T source); +} diff --git a/Source/Kernel/Events.Projections/Changes/IChangesetStorage.cs b/Source/Kernel/Events.Projections/Changes/IChangesetStorage.cs index f25de0103..53b69178b 100644 --- a/Source/Kernel/Events.Projections/Changes/IChangesetStorage.cs +++ b/Source/Kernel/Events.Projections/Changes/IChangesetStorage.cs @@ -1,6 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Changes; using Cratis.Execution; namespace Cratis.Events.Projections.Changes @@ -14,8 +15,8 @@ public interface IChangesetStorage /// Save changesets associated with a specific . /// /// to save for. - /// All the associated changesets. + /// All the associated changesets. /// Async task. - Task Save(CorrelationId correlationId, IEnumerable associatedChangesets); + Task Save(CorrelationId correlationId, IEnumerable> associatedChangesets); } } diff --git a/Source/Kernel/Events.Projections/Changes/NullChangesetStorage.cs b/Source/Kernel/Events.Projections/Changes/NullChangesetStorage.cs index a8e0d89cd..abe4213b0 100644 --- a/Source/Kernel/Events.Projections/Changes/NullChangesetStorage.cs +++ b/Source/Kernel/Events.Projections/Changes/NullChangesetStorage.cs @@ -1,6 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Changes; using Cratis.Execution; namespace Cratis.Events.Projections.Changes @@ -11,6 +12,6 @@ namespace Cratis.Events.Projections.Changes public class NullChangesetStorage : IChangesetStorage { /// - public Task Save(CorrelationId correlationId, IEnumerable associatedChangesets) => Task.CompletedTask; + public Task Save(CorrelationId correlationId, IEnumerable> associatedChangesets) => Task.CompletedTask; } } diff --git a/Source/Kernel/Events.Projections/Definitions/ChildrenDefinition.cs b/Source/Kernel/Events.Projections/Definitions/ChildrenDefinition.cs index 1f4517c20..f0e84ff69 100644 --- a/Source/Kernel/Events.Projections/Definitions/ChildrenDefinition.cs +++ b/Source/Kernel/Events.Projections/Definitions/ChildrenDefinition.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Definitions { /// diff --git a/Source/Kernel/Events.Projections/Definitions/FromDefinition.cs b/Source/Kernel/Events.Projections/Definitions/FromDefinition.cs index b89c9e431..3bf2a4781 100644 --- a/Source/Kernel/Events.Projections/Definitions/FromDefinition.cs +++ b/Source/Kernel/Events.Projections/Definitions/FromDefinition.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Definitions { /// diff --git a/Source/Kernel/Events.Projections/Definitions/ProjectionDefinition.cs b/Source/Kernel/Events.Projections/Definitions/ProjectionDefinition.cs index f301c1d34..9985c5419 100644 --- a/Source/Kernel/Events.Projections/Definitions/ProjectionDefinition.cs +++ b/Source/Kernel/Events.Projections/Definitions/ProjectionDefinition.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Definitions { /// diff --git a/Source/Kernel/Events.Projections/EventContext.cs b/Source/Kernel/Events.Projections/EventContext.cs index 71bbfb2b1..61f41b68e 100644 --- a/Source/Kernel/Events.Projections/EventContext.cs +++ b/Source/Kernel/Events.Projections/EventContext.cs @@ -1,7 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Cratis.Events.Projections.Changes; +using Cratis.Changes; namespace Cratis.Events.Projections { @@ -9,6 +9,6 @@ namespace Cratis.Events.Projections /// Represents the context of an event when being handled by a . /// /// The that occurred. - /// The to build on. - public record EventContext(Event Event, Changeset Changeset); + /// The to build on. + public record EventContext(Event Event, Changeset Changeset); } diff --git a/Source/Kernel/Events.Projections/EventTypeWithKeyResolver.cs b/Source/Kernel/Events.Projections/EventTypeWithKeyResolver.cs index 9392a5f4b..2aa4e7601 100644 --- a/Source/Kernel/Events.Projections/EventTypeWithKeyResolver.cs +++ b/Source/Kernel/Events.Projections/EventTypeWithKeyResolver.cs @@ -1,12 +1,14 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections { /// /// Represents a combination of and . /// /// The . - /// The . - public record EventTypeWithKeyResolver(EventType EventType, EventValueProvider KeyResolver); + /// The for resolving the key. + public record EventTypeWithKeyResolver(EventType EventType, ValueProvider KeyResolver); } diff --git a/Source/Kernel/Events.Projections/EventValueProvider.cs b/Source/Kernel/Events.Projections/EventValueProvider.cs deleted file mode 100644 index 25df24bc3..000000000 --- a/Source/Kernel/Events.Projections/EventValueProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Cratis. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Cratis.Events.Projections -{ - /// - /// Represents the delegate for providing a value from an event. - /// - /// to resolve from. - /// Resolved value. - public delegate object EventValueProvider(Event @event); -} diff --git a/Source/Kernel/Events.Projections/EventValueProviders.cs b/Source/Kernel/Events.Projections/EventValueProviders.cs index 3105529bf..7bcdc90b5 100644 --- a/Source/Kernel/Events.Projections/EventValueProviders.cs +++ b/Source/Kernel/Events.Projections/EventValueProviders.cs @@ -1,25 +1,27 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections { /// - /// Represents utilities for creating instances for different scenarios. + /// Represents utilities for creating instances for providing values from events. /// public static class EventValueProviders { /// - /// Create a that can copy the content of the events event source id from within the content of an event to a target property. + /// Create a that can copy the content of the events event source id from within the content of an event to a target property. /// - /// A new . - public static readonly EventValueProvider FromEventSourceId = (Event @event) => @event.EventSourceId.ToString(); + /// A new . + public static readonly ValueProvider FromEventSourceId = (Event @event) => @event.EventSourceId.ToString(); /// - /// Create a that can copy the content of a property from within the content of an event to a target property. + /// Create a that can copy the content of a property from within the content of an event to a target property. /// /// Source property. - /// A new . - public static EventValueProvider FromEventContent(string sourceProperty) + /// A new . + public static ValueProvider FromEventContent(string sourceProperty) { var sourcePath = sourceProperty.Split('.'); diff --git a/Source/Kernel/Events.Projections/ExpandoObjectExtensions.cs b/Source/Kernel/Events.Projections/ExpandoObjectExtensions.cs deleted file mode 100644 index a910825dd..000000000 --- a/Source/Kernel/Events.Projections/ExpandoObjectExtensions.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Cratis. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections; -using System.Dynamic; - -namespace Cratis.Events.Projections -{ - /// - /// Extension methods for working with . - /// - public static class ExpandoObjectExtensions - { - /// - /// Ensure a specific path for a exists on an .. - /// - /// Target . - /// to get or create for. - /// at property. - public static ExpandoObject EnsurePath(this ExpandoObject target, Property property) - { - var currentTarget = target as IDictionary; - for (var propertyIndex = 0; propertyIndex < property.Segments.Length - 1; propertyIndex++) - { - var segment = property.Segments[propertyIndex]; - if (!currentTarget.ContainsKey(segment)) - { - var nested = new ExpandoObject(); - currentTarget[segment] = nested; - currentTarget = nested!; - } - else - { - currentTarget = ((ExpandoObject)currentTarget[segment])!; - } - } - - return (currentTarget as ExpandoObject)!; - } - - /// - /// Ensures that a collection exists for a specific . - /// - /// Target . - /// to ensure collection for. - /// The ensured . - /// Thrown if there is an existing property and it is not enumerable. - public static ICollection EnsureCollection(this ExpandoObject target, Property childrenProperty) - { - var inner = target.EnsurePath(childrenProperty) as IDictionary; - if (!inner.ContainsKey(childrenProperty.LastSegment)) - { - inner[childrenProperty.LastSegment] = new List(); - } - - if (!(inner[childrenProperty.LastSegment] is IEnumerable)) - { - throw new ChildrenPropertyIsNotEnumerable(childrenProperty); - } - - var items = (inner[childrenProperty.LastSegment] as IEnumerable)!.Cast(); - if (items is not IList) - { - items = new List(items!); - } - inner[childrenProperty.LastSegment] = items; - return (items as ICollection)!; - } - - /// - /// Check if there is an item with a specific key in a collection of items. - /// - /// Items to check. - /// holding identity on each item. - /// The key value to check for. - /// True if there is an item, false if not - public static bool Contains(this IEnumerable items, Property identityProperty, object key) => - items!.Any((IDictionary _) => _.ContainsKey(identityProperty.Path) && _[identityProperty.Path].Equals(key)); - - /// - /// Find an item in a collection by its identity. - /// - /// Items to find from. - /// holding identity on each item. - /// The key value to check for. - /// The item or default if not found. - public static ExpandoObject? FindByKey(this IEnumerable items, Property identityProperty, object key) => - items!.FirstOrDefault((IDictionary _) => _.ContainsKey(identityProperty.Path) && _[identityProperty.Path].Equals(key)) as ExpandoObject; - } -} diff --git a/Source/Kernel/Events.Projections/Expressions/AddExpressionResolver.cs b/Source/Kernel/Events.Projections/Expressions/AddExpressionResolver.cs index 2344edf4f..8e35bfd75 100644 --- a/Source/Kernel/Events.Projections/Expressions/AddExpressionResolver.cs +++ b/Source/Kernel/Events.Projections/Expressions/AddExpressionResolver.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Text.RegularExpressions; +using Cratis.Properties; namespace Cratis.Events.Projections.Expressions { @@ -16,7 +17,7 @@ public class AddExpressionResolver : IPropertyMapperExpressionResolver public bool CanResolve(Property targetProperty, string expression) => _regularExpression.Match(expression).Success; /// - public PropertyMapper Resolve(Property targetProperty, string expression) + public PropertyMapper Resolve(Property targetProperty, string expression) { var match = _regularExpression.Match(expression); return PropertyMappers.AddWithEventValueProvider(targetProperty, EventValueProviders.FromEventContent(match.Groups[1].Value)); diff --git a/Source/Kernel/Events.Projections/Expressions/EventSourceIdExpressionResolver.cs b/Source/Kernel/Events.Projections/Expressions/EventSourceIdExpressionResolver.cs index 1f09779af..1d7a37a30 100644 --- a/Source/Kernel/Events.Projections/Expressions/EventSourceIdExpressionResolver.cs +++ b/Source/Kernel/Events.Projections/Expressions/EventSourceIdExpressionResolver.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Expressions { /// @@ -12,6 +14,6 @@ public class EventSourceIdExpressionResolver : IPropertyMapperExpressionResolver public bool CanResolve(Property targetProperty, string expression) => expression == "$eventSourceId"; /// - public PropertyMapper Resolve(Property targetProperty, string _) => PropertyMappers.FromEventValueProvider(targetProperty, EventValueProviders.FromEventSourceId); + public PropertyMapper Resolve(Property targetProperty, string _) => PropertyMappers.FromEventValueProvider(targetProperty, EventValueProviders.FromEventSourceId); } } diff --git a/Source/Kernel/Events.Projections/Expressions/IEventValueProviderExpressionResolver.cs b/Source/Kernel/Events.Projections/Expressions/IEventValueProviderExpressionResolver.cs index 58d4e018c..e83ae95ce 100644 --- a/Source/Kernel/Events.Projections/Expressions/IEventValueProviderExpressionResolver.cs +++ b/Source/Kernel/Events.Projections/Expressions/IEventValueProviderExpressionResolver.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Expressions { /// @@ -19,7 +21,7 @@ public interface IEventValueProviderExpressionResolver /// Called to resolve the expression. /// /// Expression to resolve. - /// it resolves to. - EventValueProvider Resolve(string expression); + /// it resolves to. + ValueProvider Resolve(string expression); } } diff --git a/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolver.cs b/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolver.cs index b66a64f7e..d2c960e02 100644 --- a/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolver.cs +++ b/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolver.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Expressions { /// @@ -21,7 +23,7 @@ public interface IPropertyMapperExpressionResolver /// /// The target property we're mapping to. /// Expression to resolve - /// it resolves to. - PropertyMapper Resolve(Property targetProperty, string expression); + /// it resolves to. + PropertyMapper Resolve(Property targetProperty, string expression); } } diff --git a/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolvers.cs b/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolvers.cs index 63c4cd94d..ce6d2f9d8 100644 --- a/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolvers.cs +++ b/Source/Kernel/Events.Projections/Expressions/IPropertyMapperExpressionResolvers.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Expressions { /// @@ -21,7 +23,7 @@ public interface IPropertyMapperExpressionResolvers /// /// The target property we're mapping to. /// Expression to resolve - /// it resolves to. - PropertyMapper Resolve(Property targetProperty, string expression); + /// it resolves to. + PropertyMapper Resolve(Property targetProperty, string expression); } } diff --git a/Source/Kernel/Events.Projections/Expressions/PropertyMapperExpressionResolvers.cs b/Source/Kernel/Events.Projections/Expressions/PropertyMapperExpressionResolvers.cs index f5f4c6d55..75ed8fbb5 100644 --- a/Source/Kernel/Events.Projections/Expressions/PropertyMapperExpressionResolvers.cs +++ b/Source/Kernel/Events.Projections/Expressions/PropertyMapperExpressionResolvers.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Expressions { /// @@ -20,7 +22,7 @@ public class PropertyMapperExpressionResolvers : IPropertyMapperExpressionResolv public bool CanResolve(Property targetProperty, string expression) => _resolvers.Any(_ => _.CanResolve(targetProperty, expression)); /// - public PropertyMapper Resolve(Property targetProperty, string expression) + public PropertyMapper Resolve(Property targetProperty, string expression) { var resolver = Array.Find(_resolvers, _ => _.CanResolve(targetProperty, expression)); ThrowIfUnsupportedEventValueExpression(expression, resolver); diff --git a/Source/Kernel/Events.Projections/Expressions/PropertyOnEventContentExpressionProvider.cs b/Source/Kernel/Events.Projections/Expressions/PropertyOnEventContentExpressionProvider.cs index ffd5b05d3..60c48d39d 100644 --- a/Source/Kernel/Events.Projections/Expressions/PropertyOnEventContentExpressionProvider.cs +++ b/Source/Kernel/Events.Projections/Expressions/PropertyOnEventContentExpressionProvider.cs @@ -1,6 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections.Expressions { /// @@ -12,6 +14,6 @@ public class PropertyOnEventContentExpressionProvider : IPropertyMapperExpressio public bool CanResolve(Property targetProperty, string expression) => !expression.StartsWith("$", StringComparison.InvariantCultureIgnoreCase); /// - public PropertyMapper Resolve(Property targetProperty, string expression) => PropertyMappers.FromEventValueProvider(targetProperty, EventValueProviders.FromEventContent(expression)); + public PropertyMapper Resolve(Property targetProperty, string expression) => PropertyMappers.FromEventValueProvider(targetProperty, EventValueProviders.FromEventContent(expression)); } } diff --git a/Source/Kernel/Events.Projections/Expressions/SubtractExpressionResolver.cs b/Source/Kernel/Events.Projections/Expressions/SubtractExpressionResolver.cs index 6268aacdf..e98acc993 100644 --- a/Source/Kernel/Events.Projections/Expressions/SubtractExpressionResolver.cs +++ b/Source/Kernel/Events.Projections/Expressions/SubtractExpressionResolver.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Text.RegularExpressions; +using Cratis.Properties; namespace Cratis.Events.Projections.Expressions { @@ -16,7 +17,7 @@ public class SubtractExpressionResolver : IPropertyMapperExpressionResolver public bool CanResolve(Property targetProperty, string expression) => _regularExpression.Match(expression).Success; /// - public PropertyMapper Resolve(Property targetProperty, string expression) + public PropertyMapper Resolve(Property targetProperty, string expression) { var match = _regularExpression.Match(expression); return PropertyMappers.SubtractWithEventValueProvider(targetProperty, EventValueProviders.FromEventContent(match.Groups[1].Value)); diff --git a/Source/Kernel/Events.Projections/IProjection.cs b/Source/Kernel/Events.Projections/IProjection.cs index de9b890a8..52d41ae26 100644 --- a/Source/Kernel/Events.Projections/IProjection.cs +++ b/Source/Kernel/Events.Projections/IProjection.cs @@ -1,7 +1,8 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Cratis.Events.Projections.Changes; +using Cratis.Changes; +using Cratis.Properties; namespace Cratis.Events.Projections { @@ -51,14 +52,14 @@ public interface IProjection /// Provides the projection with a new . /// /// to provide. - /// being worked on. - void OnNext(Event @event, Changeset changeset); + /// being worked on. + void OnNext(Event @event, Changeset changeset); /// - /// Get the associated with a given . + /// Get the associated with a given . /// /// to get for. - /// The . - EventValueProvider GetKeyResolverFor(EventType eventType); + /// The . + ValueProvider GetKeyResolverFor(EventType eventType); } } diff --git a/Source/Kernel/Events.Projections/IProjectionStorage.cs b/Source/Kernel/Events.Projections/IProjectionStorage.cs index c5781d564..ffd9af769 100644 --- a/Source/Kernel/Events.Projections/IProjectionStorage.cs +++ b/Source/Kernel/Events.Projections/IProjectionStorage.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; namespace Cratis.Events.Projections { @@ -24,8 +24,8 @@ public interface IProjectionStorage /// /// to apply for. /// Key of the model to upsert. - /// All changes in the form of a . + /// All changes in the form of a . /// A representing the asynchronous operation. - Task ApplyChanges(Model model, object key, Changeset changeset); + Task ApplyChanges(Model model, object key, Changeset changeset); } } diff --git a/Source/Kernel/Events.Projections/InMemoryProjectionStorage.cs b/Source/Kernel/Events.Projections/InMemoryProjectionStorage.cs index 543507acb..e5d110247 100644 --- a/Source/Kernel/Events.Projections/InMemoryProjectionStorage.cs +++ b/Source/Kernel/Events.Projections/InMemoryProjectionStorage.cs @@ -2,8 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Changes; using Cratis.Dynamic; -using Cratis.Events.Projections.Changes; namespace Cratis.Events.Projections { @@ -33,7 +33,7 @@ public Task FindOrDefault(Model model, object key) } /// - public Task ApplyChanges(Model model, object key, Changeset changeset) + public Task ApplyChanges(Model model, object key, Changeset changeset) { var state = changeset.InitialState.Clone(); diff --git a/Source/Kernel/Events.Projections/Json/JsonProjectionSerializer.cs b/Source/Kernel/Events.Projections/Json/JsonProjectionSerializer.cs index 560ba1756..aa505472b 100644 --- a/Source/Kernel/Events.Projections/Json/JsonProjectionSerializer.cs +++ b/Source/Kernel/Events.Projections/Json/JsonProjectionSerializer.cs @@ -4,6 +4,7 @@ using Cratis.Concepts; using Cratis.Events.Projections.Definitions; using Cratis.Events.Projections.Expressions; +using Cratis.Properties; using Newtonsoft.Json; using Newtonsoft.Json.Schema; diff --git a/Source/Kernel/Events.Projections/Json/PropertyChildrenDefinitionDictionaryJsonConverter.cs b/Source/Kernel/Events.Projections/Json/PropertyChildrenDefinitionDictionaryJsonConverter.cs index b4af66210..f1f81b4a5 100644 --- a/Source/Kernel/Events.Projections/Json/PropertyChildrenDefinitionDictionaryJsonConverter.cs +++ b/Source/Kernel/Events.Projections/Json/PropertyChildrenDefinitionDictionaryJsonConverter.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json; using Cratis.Events.Projections.Definitions; +using Cratis.Properties; namespace Cratis.Events.Projections.Json { diff --git a/Source/Kernel/Events.Projections/Json/PropertyExpressionDictionaryJsonConverter.cs b/Source/Kernel/Events.Projections/Json/PropertyExpressionDictionaryJsonConverter.cs index e512e3686..90b284f02 100644 --- a/Source/Kernel/Events.Projections/Json/PropertyExpressionDictionaryJsonConverter.cs +++ b/Source/Kernel/Events.Projections/Json/PropertyExpressionDictionaryJsonConverter.cs @@ -1,6 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; using Newtonsoft.Json; namespace Cratis.Events.Projections.Json diff --git a/Source/Kernel/Events.Projections/MissingKeyResolverForEventType.cs b/Source/Kernel/Events.Projections/MissingKeyResolverForEventType.cs index e25d77a32..40a729682 100644 --- a/Source/Kernel/Events.Projections/MissingKeyResolverForEventType.cs +++ b/Source/Kernel/Events.Projections/MissingKeyResolverForEventType.cs @@ -1,10 +1,12 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; + namespace Cratis.Events.Projections { /// - /// Exception that gets thrown when there is no providing the key value for a specific . + /// Exception that gets thrown when there is no providing the key value for a specific . /// public class MissingKeyResolverForEventType : Exception { diff --git a/Source/Kernel/Events.Projections/MongoDB/MongoDBChangesetStorage.cs b/Source/Kernel/Events.Projections/MongoDB/MongoDBChangesetStorage.cs index 8c529c3e9..33d992f93 100644 --- a/Source/Kernel/Events.Projections/MongoDB/MongoDBChangesetStorage.cs +++ b/Source/Kernel/Events.Projections/MongoDB/MongoDBChangesetStorage.cs @@ -1,6 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Changes; using Cratis.Events.Projections.Changes; using Cratis.Execution; using Cratis.Extensions.MongoDB; @@ -24,7 +25,7 @@ public MongoDBChangesetStorage(IMongoDBClientFactory clientFactory) } /// - public Task Save(CorrelationId correlationId, IEnumerable associatedChangesets) + public Task Save(CorrelationId correlationId, IEnumerable> associatedChangesets) { return Task.CompletedTask; } diff --git a/Source/Kernel/Events.Projections/MongoDB/MongoDBProjectionStorage.cs b/Source/Kernel/Events.Projections/MongoDB/MongoDBProjectionStorage.cs index 8ef3b9edc..f842df214 100644 --- a/Source/Kernel/Events.Projections/MongoDB/MongoDBProjectionStorage.cs +++ b/Source/Kernel/Events.Projections/MongoDB/MongoDBProjectionStorage.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using Cratis.Extensions.MongoDB; using Cratis.Strings; using MongoDB.Bson; @@ -49,7 +49,7 @@ public async Task FindOrDefault(Model model, object key) } /// - public async Task ApplyChanges(Model model, object key, Changeset changeset) + public async Task ApplyChanges(Model model, object key, Changeset changeset) { var updateDefinitionBuilder = Builders.Update; UpdateDefinition? updateBuilder = default; diff --git a/Source/Kernel/Events.Projections/Projection.cs b/Source/Kernel/Events.Projections/Projection.cs index ae19ecff8..5badc32c0 100644 --- a/Source/Kernel/Events.Projections/Projection.cs +++ b/Source/Kernel/Events.Projections/Projection.cs @@ -3,7 +3,8 @@ using System.Reactive.Linq; using System.Reactive.Subjects; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; +using Cratis.Properties; namespace Cratis.Events.Projections { @@ -13,7 +14,7 @@ namespace Cratis.Events.Projections public class Projection : IProjection { readonly ISubject _subject = new Subject(); - readonly IDictionary _eventTypesToKeyResolver; + readonly IDictionary> _eventTypesToKeyResolver; /// /// Initializes a new instance of the class. @@ -61,14 +62,14 @@ public Projection( public IEnumerable ChildProjections { get; } /// - public void OnNext(Event @event, Changeset changeset) + public void OnNext(Event @event, Changeset changeset) { var context = new EventContext(@event, changeset); _subject.OnNext(context); } /// - public EventValueProvider GetKeyResolverFor(EventType eventType) + public ValueProvider GetKeyResolverFor(EventType eventType) { ThrowIfMissingKeyResolverForEventType(eventType); return _eventTypesToKeyResolver[eventType]; diff --git a/Source/Kernel/Events.Projections/ProjectionExtensions.cs b/Source/Kernel/Events.Projections/ProjectionExtensions.cs index e5208c4fe..3e26e4d85 100644 --- a/Source/Kernel/Events.Projections/ProjectionExtensions.cs +++ b/Source/Kernel/Events.Projections/ProjectionExtensions.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reactive.Linq; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; +using Cratis.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections { @@ -22,7 +24,7 @@ public static IObservable From(this IObservable obse return observable.Where(_ => _.Event.Type == eventType); } - public static IObservable Child(this IObservable observable, Property childrenProperty, Property identifiedByProperty, EventValueProvider keyResolver, IEnumerable propertyMappers) + public static IObservable Child(this IObservable observable, Property childrenProperty, Property identifiedByProperty, ValueProvider keyResolver, IEnumerable> propertyMappers) { observable.Subscribe(_ => { @@ -36,7 +38,7 @@ public static IObservable Child(this IObservable obs return observable; } - public static IObservable Project(this IObservable observable, Property childrenProperty, Property identifiedByProperty, EventValueProvider keyResolver, IEnumerable propertyMappers) + public static IObservable Project(this IObservable observable, Property childrenProperty, Property identifiedByProperty, ValueProvider keyResolver, IEnumerable> propertyMappers) { if (childrenProperty.IsRoot) { @@ -63,7 +65,7 @@ public static IObservable RemovedWith(this IObservable Join(this IObservable observable, EventType eventType, PropertyAccessor propertyResolver) + public static IObservable Join(this IObservable observable, EventType eventType) { return observable.Where(_ => _.Event.Type == eventType); } diff --git a/Source/Kernel/Events.Projections/ProjectionPipeline.cs b/Source/Kernel/Events.Projections/ProjectionPipeline.cs index 01b4dce61..d38eac1f9 100644 --- a/Source/Kernel/Events.Projections/ProjectionPipeline.cs +++ b/Source/Kernel/Events.Projections/ProjectionPipeline.cs @@ -1,6 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Changes; using Cratis.Events.Projections.Changes; using Cratis.Execution; using Microsoft.Extensions.Logging; @@ -52,7 +53,7 @@ public void Start() { _logger.HandlingEvent(@event.SequenceNumber); var correlationId = CorrelationId.New(); - var changesets = new List(); + var changesets = new List>(); var tasks = _storageProviders.Select(storage => Task.Run(async () => await HandleEventFor(Projection, storage, @event, changesets))); Task.WaitAll(tasks.ToArray()); _changesetStorage.Save(correlationId, changesets).Wait(); @@ -68,13 +69,13 @@ public void Start() /// public void StoreIn(IProjectionStorage storageProvider) => _storageProviders.Add(storageProvider); - async Task HandleEventFor(IProjection projection, IProjectionStorage storage, Event @event, List changesets) + async Task HandleEventFor(IProjection projection, IProjectionStorage storage, Event @event, List> changesets) { var keyResolver = projection.GetKeyResolverFor(@event.Type); var key = keyResolver(@event); _logger.GettingInitialValues(@event.SequenceNumber); var initialState = await storage.FindOrDefault(Projection.Model, key); - var changeset = new Changeset(@event, initialState); + var changeset = new Changeset(@event, initialState); changesets.Add(changeset); _logger.Projecting(@event.SequenceNumber); projection.OnNext(@event, changeset); diff --git a/Source/Kernel/Events.Projections/PropertyAccessor.cs b/Source/Kernel/Events.Projections/PropertyAccessor.cs deleted file mode 100644 index e76a06b90..000000000 --- a/Source/Kernel/Events.Projections/PropertyAccessor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Cratis. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Dynamic; - -namespace Cratis.Events.Projections -{ - /// - /// Delegate that represents getting a value from a . - /// - /// Input . - /// Value. - public delegate object PropertyAccessor(ExpandoObject input); -} diff --git a/Source/Kernel/Events.Projections/PropertyMapper.cs b/Source/Kernel/Events.Projections/PropertyMapper.cs deleted file mode 100644 index daada5ab6..000000000 --- a/Source/Kernel/Events.Projections/PropertyMapper.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Cratis. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Dynamic; - -namespace Cratis.Events.Projections -{ - /// - /// Represents the delegate of an operation that maps data into a target object. - /// - /// in context. - /// target to write to. - public delegate void PropertyMapper(Event @event, ExpandoObject target); -} diff --git a/Source/Kernel/Events.Projections/PropertyMappers.cs b/Source/Kernel/Events.Projections/PropertyMappers.cs index 3e958448f..fe3a05f27 100644 --- a/Source/Kernel/Events.Projections/PropertyMappers.cs +++ b/Source/Kernel/Events.Projections/PropertyMappers.cs @@ -2,21 +2,23 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections { /// - /// Represents utilities for creating for different scenarios. + /// Represents utilities for creating for different scenarios. /// public static class PropertyMappers { /// - /// Create a that can copies content provided by a to a target property. + /// Create a that can copies content provided by a to a target property. /// /// Target property. - /// to use as source. - /// A new . - public static PropertyMapper FromEventValueProvider(Property targetProperty, EventValueProvider eventValueProvider) + /// to use as source. + /// A new . + public static PropertyMapper FromEventValueProvider(Property targetProperty, ValueProvider eventValueProvider) { return (Event @event, ExpandoObject target) => { @@ -26,12 +28,12 @@ public static PropertyMapper FromEventValueProvider(Property targetProperty, Eve } /// - /// Create a that can add a property with a value provided by a onto a target property. + /// Create a that can add a property with a value provided by a onto a target property. /// /// Target property. - /// to use as source. - /// A new . - public static PropertyMapper AddWithEventValueProvider(Property targetProperty, EventValueProvider eventValueProvider) + /// to use as source. + /// A new . + public static PropertyMapper AddWithEventValueProvider(Property targetProperty, ValueProvider eventValueProvider) { return (Event @event, ExpandoObject target) => { @@ -48,12 +50,12 @@ public static PropertyMapper AddWithEventValueProvider(Property targetProperty, } /// - /// Create a that can add a property with a value provided by a onto a target property. + /// Create a that can add a property with a value provided by a onto a target property. /// /// Target property. - /// to use as source. - /// A new . - public static PropertyMapper SubtractWithEventValueProvider(Property targetProperty, EventValueProvider eventValueProvider) + /// to use as source. + /// A new . + public static PropertyMapper SubtractWithEventValueProvider(Property targetProperty, ValueProvider eventValueProvider) { return (Event @event, ExpandoObject target) => { diff --git a/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_cause_changes.cs b/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_cause_changes.cs index 9fb14cf5c..389d775d2 100644 --- a/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_cause_changes.cs +++ b/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_cause_changes.cs @@ -2,16 +2,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; +using Cratis.Properties; namespace Cratis.Events.Projections { public class when_applying_properties_that_cause_changes : Specification { - Changeset changeset; + Changeset changeset; ExpandoObject initial_state; Event @event; - IEnumerable property_mappers; + IEnumerable> property_mappers; Mock projection; void Establish() @@ -25,7 +26,7 @@ void Establish() nested.Integer = 43; nested.String = "Forty Three"; - property_mappers = new PropertyMapper[] + property_mappers = new PropertyMapper[] { (_, target) => ((dynamic)target).Integer = 44, (_, target) => ((dynamic)target).String = "Forty Four", @@ -35,7 +36,7 @@ void Establish() @event = new Event(0, "some event", DateTimeOffset.UtcNow, string.Empty, new ExpandoObject()); - changeset = new Changeset(@event, initial_state); + changeset = new(@event, initial_state); } void Because() => changeset.ApplyProperties(property_mappers); diff --git a/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_does_not_cause_changes.cs b/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_does_not_cause_changes.cs index 2a100594d..c3990cfc5 100644 --- a/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_does_not_cause_changes.cs +++ b/Specifications/Kernel/Events.Projections/for_Changeset/when_applying_properties_that_does_not_cause_changes.cs @@ -2,16 +2,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; +using Cratis.Properties; namespace Cratis.Events.Projections { public class when_applying_properties_that_does_not_cause_changes : Specification { - Changeset changeset; + Changeset changeset; ExpandoObject initial_state; Event @event; - IEnumerable property_mappers; + IEnumerable> property_mappers; Mock projection; void Establish() @@ -25,7 +26,7 @@ void Establish() nested.Integer = 43; nested.String = "Forty Three"; - property_mappers = new PropertyMapper[] + property_mappers = new PropertyMapper[] { (_, target) => ((dynamic)target).Integer = 42, (_, target) => ((dynamic)target).String = "Forty Two", @@ -35,7 +36,7 @@ void Establish() @event = new Event(0, "some event", DateTimeOffset.UtcNow, string.Empty, new ExpandoObject()); - changeset = new Changeset(@event, initial_state); + changeset = new(@event, initial_state); } void Because() => changeset.ApplyProperties(property_mappers); diff --git a/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_event_source_id.cs b/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_event_source_id.cs index ea214fed1..e8f721bb8 100644 --- a/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_event_source_id.cs +++ b/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_event_source_id.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { @@ -9,7 +10,7 @@ public class when_getting_event_source_id : Specification { static EventSourceId eventSourceId = "2f005aaf-2f4e-4a47-92ea-63687ef74bd4"; - EventValueProvider value_provider; + ValueProvider value_provider; Event @event; object result; diff --git a/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_from_event_content_nested_property.cs b/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_from_event_content_nested_property.cs index dc75b24bd..039c4f573 100644 --- a/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_from_event_content_nested_property.cs +++ b/Specifications/Kernel/Events.Projections/for_EventValueProviders/when_getting_from_event_content_nested_property.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { public class when_getting_from_event_content_nested_property : Specification { - EventValueProvider value_provider; + ValueProvider value_provider; Event @event; object result; diff --git a/Specifications/Kernel/Events.Projections/for_Projection/when_getting_key_resolver_for_event_type.cs b/Specifications/Kernel/Events.Projections/for_Projection/when_getting_key_resolver_for_event_type.cs index d4f273e60..0537bcd1e 100644 --- a/Specifications/Kernel/Events.Projections/for_Projection/when_getting_key_resolver_for_event_type.cs +++ b/Specifications/Kernel/Events.Projections/for_Projection/when_getting_key_resolver_for_event_type.cs @@ -1,6 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Properties; using Newtonsoft.Json.Schema; namespace Cratis.Events.Projections.for_Projection @@ -9,8 +10,8 @@ public class when_getting_key_resolver_for_event_type : Specification { static EventType event_type = "993888cc-a9c5-4d56-ae21-f732159feec7"; Projection projection; - EventValueProvider expected; - EventValueProvider result; + ValueProvider expected; + ValueProvider result; void Establish() { diff --git a/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_not_of_interest.cs b/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_not_of_interest.cs index 2bd676abd..fba26745e 100644 --- a/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_not_of_interest.cs +++ b/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_not_of_interest.cs @@ -1,7 +1,7 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using Newtonsoft.Json.Schema; namespace Cratis.Events.Projections.for_Projection @@ -11,7 +11,7 @@ public class when_next_event_is_not_of_interest : Specification Projection projection; bool observed; Event @event; - Changeset changeset; + Changeset changeset; void Establish() { @@ -31,7 +31,7 @@ void Establish() "30c1ebf5-cc30-4216-afed-e3e0aefa1316", new()); - changeset = new Changeset(@event, new()); + changeset = new(@event, new()); projection.Event.Subscribe(_ => observed = true); } diff --git a/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_of_interest.cs b/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_of_interest.cs index dc9501144..e3ee165a4 100644 --- a/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_of_interest.cs +++ b/Specifications/Kernel/Events.Projections/for_Projection/when_next_event_is_of_interest.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using Newtonsoft.Json.Schema; namespace Cratis.Events.Projections.for_Projection @@ -16,10 +16,10 @@ public class when_next_event_is_of_interest : Specification ExpandoObject initial_state; Event first_event; - Changeset first_changeset; + Changeset first_changeset; Event second_event; - Changeset second_changeset; + Changeset second_changeset; void Establish() { @@ -42,7 +42,7 @@ void Establish() "30c1ebf5-cc30-4216-afed-e3e0aefa1316", new()); - first_changeset = new Changeset(first_event, new()); + first_changeset = new(first_event, new()); second_event = new Event( 0, @@ -50,7 +50,7 @@ void Establish() DateTimeOffset.UtcNow, "30c1ebf5-cc30-4216-afed-e3e0aefa1316", new ()); - second_changeset = new Changeset(second_event, initial_state); + second_changeset = new(second_event, initial_state); observed_events = new(); projection.Event.Subscribe(_ => observed_events.Add(_)); diff --git a/Specifications/Kernel/Events.Projections/for_ProjectionPipeline/when_events_are_provided_and_we_have_two_storage_providers.cs b/Specifications/Kernel/Events.Projections/for_ProjectionPipeline/when_events_are_provided_and_we_have_two_storage_providers.cs index 028212316..4de3dd67c 100644 --- a/Specifications/Kernel/Events.Projections/for_ProjectionPipeline/when_events_are_provided_and_we_have_two_storage_providers.cs +++ b/Specifications/Kernel/Events.Projections/for_ProjectionPipeline/when_events_are_provided_and_we_have_two_storage_providers.cs @@ -3,7 +3,7 @@ using System.Dynamic; using System.Reactive.Subjects; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using Newtonsoft.Json.Schema; namespace Cratis.Events.Projections.for_ProjectionPipeline @@ -40,8 +40,8 @@ void Establish() [Fact] void should_find_model_using_key_for_first_storage() => first_storage.Verify(_ => _.FindOrDefault(model, key), Once()); [Fact] void should_find_model_using_key_for_second_storage() => second_storage.Verify(_ => _.FindOrDefault(model, key), Once()); - [Fact] void should_call_projection_for_both_storages() => projection.Verify(_ => _.OnNext(@event, IsAny()), Exactly(2)); - [Fact] void should_apply_changes_for_first_storage() => first_storage.Verify(_ => _.ApplyChanges(model, key, IsAny()), Once()); - [Fact] void should_apply_changes_for_second_storage() => second_storage.Verify(_ => _.ApplyChanges(model, key, IsAny()), Once()); + [Fact] void should_call_projection_for_both_storages() => projection.Verify(_ => _.OnNext(@event, IsAny>()), Exactly(2)); + [Fact] void should_apply_changes_for_first_storage() => first_storage.Verify(_ => _.ApplyChanges(model, key, IsAny>()), Once()); + [Fact] void should_apply_changes_for_second_storage() => second_storage.Verify(_ => _.ApplyChanges(model, key, IsAny>()), Once()); } } diff --git a/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_both_sides_has_the_value.cs b/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_both_sides_has_the_value.cs index 2dbd2d823..e6b4d42a5 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_both_sides_has_the_value.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_both_sides_has_the_value.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using ObjectsComparer; namespace Cratis.Events.Projections.for_PropertyDifference diff --git a/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_changed_has_the_value_and_original_does_not.cs b/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_changed_has_the_value_and_original_does_not.cs index 35c65d7af..ffe27ca13 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_changed_has_the_value_and_original_does_not.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_changed_has_the_value_and_original_does_not.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using ObjectsComparer; namespace Cratis.Events.Projections.for_PropertyDifference diff --git a/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_original_has_the_value_and_changed_does_not.cs b/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_original_has_the_value_and_changed_does_not.cs index d8782a0ce..40dcb9997 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_original_has_the_value_and_changed_does_not.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyDifference/when_original_has_the_value_and_changed_does_not.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; -using Cratis.Events.Projections.Changes; +using Cratis.Changes; using ObjectsComparer; namespace Cratis.Events.Projections.for_PropertyDifference diff --git a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs index 77beddcc8..618613236 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { public class when_adding_to_a_deep_nested_property_with_existing_value_from_an_event_value_provider : Specification { - PropertyMapper property_mapper; + PropertyMapper property_mapper; Event @event; ExpandoObject result; Event provided_event; diff --git a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs index ce9ade77a..4c9cfb1cc 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_adding_to_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { public class when_adding_to_a_deep_nested_property_without_existing_value_from_an_event_value_provider : Specification { - PropertyMapper property_mapper; + PropertyMapper property_mapper; Event @event; ExpandoObject result; Event provided_event; diff --git a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_mapping_to_a_deep_nested_property_from_an_event_value_provider.cs b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_mapping_to_a_deep_nested_property_from_an_event_value_provider.cs index 491857750..5b02394e1 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_mapping_to_a_deep_nested_property_from_an_event_value_provider.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_mapping_to_a_deep_nested_property_from_an_event_value_provider.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { public class when_mapping_to_a_deep_nested_property_from_an_event_value_provider : Specification { - PropertyMapper property_mapper; + PropertyMapper property_mapper; Event @event; ExpandoObject result; Event provided_event; diff --git a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs index cdc454015..564c4441f 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_with_existing_value_from_an_event_value_provider.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { public class when_subtracting_a_deep_nested_property_with_existing_value_from_an_event_value_provider : Specification { - PropertyMapper property_mapper; + PropertyMapper property_mapper; Event @event; ExpandoObject result; Event provided_event; diff --git a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs index 802bcb3dc..83c3f298e 100644 --- a/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs +++ b/Specifications/Kernel/Events.Projections/for_PropertyMappers/when_subtracting_a_deep_nested_property_without_existing_value_from_an_event_value_provider.cs @@ -2,12 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Dynamic; +using Cratis.Properties; namespace Cratis.Events.Projections.for_PropertyMappers { public class when_subtracting_a_deep_nested_property_without_existing_value_from_an_event_value_provider : Specification { - PropertyMapper property_mapper; + PropertyMapper property_mapper; Event @event; ExpandoObject result; Event provided_event;