diff --git a/PinkParrot.sln.DotSettings b/PinkParrot.sln.DotSettings
index 6627add68..8f7549b08 100644
--- a/PinkParrot.sln.DotSettings
+++ b/PinkParrot.sln.DotSettings
@@ -33,6 +33,9 @@
False
True
+ True
+ True
+ False
SingleQuoted
==========================================================================
$FILENAME$
@@ -83,4 +86,7 @@
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
True
+ True
+ True
+ True
True
\ No newline at end of file
diff --git a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
new file mode 100644
index 000000000..750e5be11
--- /dev/null
+++ b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
@@ -0,0 +1,26 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+
+namespace PinkParrot.Modules.Api.Schemas
+{
+ public class SchemasController : Controller
+ {
+ [HttpPost]
+ [Route("schemas/")]
+ public async Task Create()
+ {
+ }
+
+ [HttpPut]
+ [Route("schemas/{name}/")]
+ public async Task Update()
+ {
+ }
+
+ [HttpDelete]
+ [Route("schemas/{name}/")]
+ public async Task Delete()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PinkParrot/Project_Readme.html b/src/PinkParrot/Project_Readme.html
deleted file mode 100644
index 1a0f5b51a..000000000
--- a/src/PinkParrot/Project_Readme.html
+++ /dev/null
@@ -1,187 +0,0 @@
-
-
-
-
- Welcome to ASP.NET Core
-
-
-
-
-
-
-
-
-
This application consists of:
-
- - Sample pages using ASP.NET Core MVC
- - Bower for managing client-side libraries
- - Theming using Bootstrap
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/PinkParrot/project.json b/src/PinkParrot/project.json
index 361cfdacb..41a3470bb 100644
--- a/src/PinkParrot/project.json
+++ b/src/PinkParrot/project.json
@@ -1,14 +1,28 @@
{
"dependencies": {
+ "Microsoft.AspNetCore.Diagnostics": "1.0.0",
+ "Microsoft.AspNetCore.Mvc": "1.0.0",
+ "Microsoft.AspNetCore.Razor.Tools": {
+ "version": "1.0.0-preview2-final",
+ "type": "build"
+ },
+ "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
+ "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
+ "Microsoft.AspNetCore.StaticFiles": "1.0.0",
+ "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
+ "Microsoft.Extensions.Configuration.Json": "1.0.0",
+ "Microsoft.Extensions.Logging": "1.0.0",
+ "Microsoft.Extensions.Logging.Console": "1.0.0",
+ "Microsoft.Extensions.Logging.Debug": "1.0.0",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
},
- "Microsoft.AspNetCore.Diagnostics": "1.0.0",
-
- "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
- "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
- "Microsoft.Extensions.Logging.Console": "1.0.0"
+ "PinkParrot.Core": "1.0.0-*",
+ "PinkParrot.Events": "1.0.0-*",
+ "PinkParrot.Infrastructure": "1.0.0-*",
+ "PinkParrot.Write": "1.0.0-*"
},
"tools": {
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs
index 816f6b296..ad4c39d26 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs
@@ -6,9 +6,7 @@
// All rights reserved.
// ==========================================================================
-using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Threading.Tasks;
using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.Tasks;
@@ -19,7 +17,7 @@ namespace PinkParrot.Core.Schema
{
public abstract class ModelField
{
- private readonly Guid id;
+ private readonly long id;
private string name;
private string hint;
private string displayName;
@@ -27,7 +25,7 @@ namespace PinkParrot.Core.Schema
private bool isDisabled;
private bool isHidden;
- public Guid Id
+ public long Id
{
get { return id; }
}
@@ -62,9 +60,9 @@ namespace PinkParrot.Core.Schema
get { return isDisabled; }
}
- protected ModelField(Guid id, string name)
+ protected ModelField(long id, string name)
{
- Guard.NotEmpty(id, nameof(id));
+ Guard.GreaterThan(id, 0, nameof(id));
Guard.ValidSlug(name, nameof(name));
this.id = id;
@@ -72,13 +70,13 @@ namespace PinkParrot.Core.Schema
this.name = name;
}
- public ModelField Configure(PropertiesBag settings, ICollection errors)
+ public ModelField Configure(dynamic settings, ICollection errors)
{
var clone = Clone();
if (settings.Contains("Name"))
{
- clone.name = settings["Name"].ToString();
+ clone.name = settings.Name;
if (!clone.name.IsSlug())
{
@@ -86,26 +84,19 @@ namespace PinkParrot.Core.Schema
}
}
- if (settings.Contains("Hint"))
+ if (settings.Contains("hint"))
{
- clone.hint = settings["Hint"].ToString()?.Trim() ?? string.Empty;
+ clone.hint = settings.Hint?.Trim();
}
- if (settings.Contains("DisplayName"))
+ if (settings.Contains("displayName"))
{
- clone.displayName = settings["DisplayName"].ToString()?.Trim() ?? string.Empty;
+ clone.displayName = settings.DisplayName?.Trim();
}
if (settings.Contains("IsRequired"))
{
- try
- {
- clone.isRequired = settings["IsRequired"].ToBoolean(CultureInfo.InvariantCulture);
- }
- catch (InvalidCastException)
- {
- errors.Add("IsRequired is not a valid boolean");
- }
+ clone.isRequired = settings.IsRequired;
}
clone.ConfigureCore(settings, errors);
@@ -113,7 +104,7 @@ namespace PinkParrot.Core.Schema
return clone;
}
- protected virtual void ConfigureCore(PropertiesBag settings, ICollection errors)
+ protected virtual void ConfigureCore(dynamic settings, ICollection errors)
{
}
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs
index e318249d9..e6cad2f5c 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs
@@ -6,13 +6,11 @@
// All rights reserved.
// ==========================================================================
-using System;
-
namespace PinkParrot.Core.Schema
{
public class ModelFieldFactory
{
- public virtual ModelField CreateField(Guid id, string type, string fieldName)
+ public virtual ModelField CreateField(long id, string type, string fieldName)
{
return new NumberField(id, fieldName);
}
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
index cf46324ee..b45f13bb2 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
@@ -18,10 +18,10 @@ namespace PinkParrot.Core.Schema
public sealed class ModelSchema
{
private readonly ModelSchemaMetadata metadata;
- private readonly ImmutableDictionary fields;
+ private readonly ImmutableDictionary fields;
private readonly Dictionary fieldsByName;
- public ModelSchema(ModelSchemaMetadata metadata, ImmutableDictionary fields)
+ public ModelSchema(ModelSchemaMetadata metadata, ImmutableDictionary fields)
{
Guard.NotNull(fields, nameof(fields));
Guard.NotNull(metadata, nameof(metadata));
@@ -40,10 +40,10 @@ namespace PinkParrot.Core.Schema
throw new DomainValidationException("Cannot create the schema.", $"'{name}' is not a valid slug.");
}
- return new ModelSchema(new ModelSchemaMetadata(name), ImmutableDictionary.Empty);
+ return new ModelSchema(new ModelSchemaMetadata(name), ImmutableDictionary.Empty);
}
- public IReadOnlyDictionary Fields
+ public IReadOnlyDictionary Fields
{
get { return fields; }
}
@@ -60,14 +60,14 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(newMetadata, fields);
}
- public ModelSchema AddField(Guid id, string type, string fieldName, ModelFieldFactory factory)
+ public ModelSchema AddField(long id, string type, string fieldName, ModelFieldFactory factory)
{
var field = factory.CreateField(id, type, fieldName);
return SetField(field);
}
- public ModelSchema SetField(Guid fieldId, PropertiesBag settings)
+ public ModelSchema SetField(long fieldId, PropertiesBag settings)
{
Guard.NotNull(settings, nameof(settings));
@@ -86,22 +86,22 @@ namespace PinkParrot.Core.Schema
});
}
- public ModelSchema DisableField(Guid fieldId)
+ public ModelSchema DisableField(long fieldId)
{
return UpdateField(fieldId, field => field.Disable());
}
- public ModelSchema EnableField(Guid fieldId)
+ public ModelSchema EnableField(long fieldId)
{
return UpdateField(fieldId, field => field.Enable());
}
- public ModelSchema HideField(Guid fieldId)
+ public ModelSchema HideField(long fieldId)
{
return UpdateField(fieldId, field => field.Show());
}
- public ModelSchema ShowField(Guid fieldId)
+ public ModelSchema ShowField(long fieldId)
{
return UpdateField(fieldId, field => field.Show());
}
@@ -118,10 +118,8 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(metadata, fields.SetItem(field.Id, field));
}
- public ModelSchema DeleteField(Guid fieldId)
+ public ModelSchema DeleteField(long fieldId)
{
- Guard.NotEmpty(fieldId, nameof(fieldId));
-
if (!fields.ContainsKey(fieldId))
{
throw new DomainValidationException($"A field with id {fieldId} does not exist.");
@@ -130,7 +128,7 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(metadata, fields.Remove(fieldId));
}
- private ModelSchema UpdateField(Guid fieldId, Func updater)
+ private ModelSchema UpdateField(long fieldId, Func updater)
{
ModelField field;
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs
index 90d0ad8e1..60e0ff146 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs
@@ -30,15 +30,15 @@ namespace PinkParrot.Core.Schema
get { return minValue; }
}
- public NumberField(Guid id, string name)
+ public NumberField(long id, string name)
: base(id, name)
{
}
- protected override void ConfigureCore(PropertiesBag settings, ICollection errors)
+ protected override void ConfigureCore(dynamic settings, ICollection errors)
{
- maxValue = ParseNumber("MaxValue", settings, errors);
- minValue = ParseNumber("MinValue", settings, errors);
+ maxValue = settings.MaxValue;
+ minValue = settings.MinValue;
if (maxValue.HasValue && minValue.HasValue && minValue.Value > maxValue.Value)
{
@@ -46,23 +46,6 @@ namespace PinkParrot.Core.Schema
}
}
- private static double? ParseNumber(string key, PropertiesBag settings, ICollection errors)
- {
- try
- {
- if (settings.Contains(key))
- {
- return settings[key].ToNullableDouble(CultureInfo.InvariantCulture);
- }
- }
- catch (InvalidCastException)
- {
- errors.Add($"'{key}' is not a valid number");
- }
-
- return null;
- }
-
protected override Task ValidateCoreAsync(PropertyValue property, ICollection errors)
{
try
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs
index ca22786d3..34b447473 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs
@@ -6,17 +6,16 @@
// All rights reserved.
// ==========================================================================
-using System;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldAdded : IEvent
{
- public Guid FieldId { get; set; }
+ public long FieldId;
public string FieldType;
- public string FieldName { get; set; }
+ public string FieldName;
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs
index 04e5609c6..fa4f616fd 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs
@@ -6,13 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldDeleted : IEvent
{
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs
index cbdaee9a2..9ced16879 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs
@@ -6,13 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldDisabled : IEvent
{
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs
index 9a8ff9e24..8c3a777bc 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs
@@ -6,13 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldEnabled : IEvent
{
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs
index fde8b7657..371572f54 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs
@@ -6,13 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldHidden : IEvent
{
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs
index 212fe4114..af1714a43 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs
@@ -6,13 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldShown : IEvent
{
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs
index e993c8068..a7071bcd0 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs
@@ -6,15 +6,14 @@
// All rights reserved.
// ==========================================================================
-using System;
using PinkParrot.Infrastructure;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
public class ModelFieldUpdated : IEvent
{
- public Guid FieldId { get; set; }
+ public long FieldId;
public PropertiesBag Settings { get; set; }
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs
index 9e9d8f8d7..1e60a854c 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs
@@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs
index 1671d9501..4e67bfbf9 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs
@@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs
index fcaaf5225..c4d345e89 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs
@@ -7,7 +7,7 @@
// ==========================================================================
using PinkParrot.Infrastructure;
-using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema
{
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTest.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs
similarity index 100%
rename from src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTest.cs
rename to src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs
new file mode 100644
index 000000000..ec89a3811
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs
@@ -0,0 +1,284 @@
+// ==========================================================================
+// DispatchingTests.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System.Threading.Tasks;
+using PinkParrot.Infrastructure.Dispatching;
+using PinkParrot.Infrastructure.Tasks;
+using Xunit;
+
+namespace PinkParrot.Infrastructure
+{
+ public sealed class DispatchingTests
+ {
+ private interface IEvent { }
+
+ private class EventA : IEvent { }
+ private class EventB : IEvent { }
+ private class Unknown : IEvent { }
+
+ private class AsyncFuncConsumer
+ {
+ public int EventATriggered { get; private set; }
+ public int EventBTriggered { get; private set; }
+
+ public Task DispatchEventAsync(IEvent @event)
+ {
+ return this.DispatchFuncAsync(@event, 9);
+ }
+
+ public Task DispatchEventAsync(IEvent @event, int context)
+ {
+ return this.DispatchFuncAsync(@event, context, 13);
+ }
+
+ public Task On(EventA @event)
+ {
+ return Task.FromResult(++EventATriggered);
+ }
+
+ public Task On(EventB @event)
+ {
+ return Task.FromResult(++EventBTriggered);
+ }
+
+ public Task On(EventA @event, int context)
+ {
+ return Task.FromResult(++EventATriggered + context);
+ }
+
+ public Task On(EventB @event, int context)
+ {
+ return Task.FromResult(++EventBTriggered + context);
+ }
+ }
+
+ private class AsyncConsumer
+ {
+ public int EventATriggered { get; private set; }
+ public int EventBTriggered { get; private set; }
+
+ public Task DispatchEventAsync(IEvent @event)
+ {
+ return this.DispatchActionAsync(@event);
+ }
+
+ public Task DispatchEventAsync(IEvent @event, int context)
+ {
+ return this.DispatchActionAsync(@event, context);
+ }
+
+ public Task On(EventA @event)
+ {
+ EventATriggered++;
+ return TaskHelper.Done;
+ }
+
+ public Task On(EventB @event)
+ {
+ EventBTriggered++;
+ return TaskHelper.Done;
+ }
+
+ public Task On(EventA @event, int context)
+ {
+ EventATriggered = EventATriggered + context;
+ return TaskHelper.Done;
+ }
+
+ public Task On(EventB @event, int context)
+ {
+ EventBTriggered = EventATriggered + context;
+ return TaskHelper.Done;
+ }
+ }
+
+ private class SyncFuncConsumer
+ {
+ public int EventATriggered { get; private set; }
+ public int EventBTriggered { get; private set; }
+
+ public int DispatchEvent(IEvent @event)
+ {
+ return this.DispatchFunc(@event, 9);
+ }
+
+ public int DispatchEvent(IEvent @event, int context)
+ {
+ return this.DispatchFunc(@event, context, 13);
+ }
+
+ public int On(EventA @event)
+ {
+ return ++EventATriggered;
+ }
+
+ public int On(EventB @event)
+ {
+ return ++EventBTriggered;
+ }
+
+ public int On(EventA @event, int context)
+ {
+ return ++EventATriggered + context;
+ }
+
+ public int On(EventB @event, int context)
+ {
+ return ++EventBTriggered + context;
+ }
+ }
+
+ private class SyncActionConsumer
+ {
+ public int EventATriggered { get; private set; }
+ public int EventBTriggered { get; private set; }
+
+ public bool DispatchEvent(IEvent @event)
+ {
+ return this.DispatchAction(@event);
+ }
+
+ public bool DispatchEvent(IEvent @event, int context)
+ {
+ return this.DispatchAction(@event, context);
+ }
+
+ public void On(EventA @event)
+ {
+ EventATriggered++;
+ }
+
+ public void On(EventB @event)
+ {
+ EventBTriggered++;
+ }
+
+ public void On(EventA @event, int context)
+ {
+ EventATriggered = EventATriggered + context;
+ }
+
+ public void On(EventB @event, int context)
+ {
+ EventBTriggered = EventATriggered + context;
+ }
+ }
+
+ [Fact]
+ public void Should_invoke_correct_event()
+ {
+ var consumer = new SyncActionConsumer();
+
+ consumer.DispatchEvent(new EventA());
+ consumer.DispatchEvent(new EventB());
+ consumer.DispatchEvent(new EventB());
+ consumer.DispatchEvent(new Unknown());
+
+ Assert.Equal(1, consumer.EventATriggered);
+ Assert.Equal(2, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public void Should_invoke_correct_event_with_context()
+ {
+ var consumer = new SyncActionConsumer();
+
+ consumer.DispatchEvent(new EventA(), 2);
+ consumer.DispatchEvent(new EventB(), 2);
+ consumer.DispatchEvent(new EventB(), 2);
+ consumer.DispatchEvent(new Unknown(), 2);
+
+ Assert.Equal(2, consumer.EventATriggered);
+ Assert.Equal(4, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public async Task Should_invoke_correct_event_asynchronously()
+ {
+ var consumer = new AsyncConsumer();
+
+ await consumer.DispatchEventAsync(new EventA());
+ await consumer.DispatchEventAsync(new EventB());
+ await consumer.DispatchEventAsync(new EventB());
+ await consumer.DispatchEventAsync(new Unknown());
+
+ Assert.Equal(1, consumer.EventATriggered);
+ Assert.Equal(2, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public async Task Should_invoke_correct_event_with_context_asynchronously()
+ {
+ var consumer = new AsyncConsumer();
+
+ await consumer.DispatchEventAsync(new EventA(), 2);
+ await consumer.DispatchEventAsync(new EventB(), 2);
+ await consumer.DispatchEventAsync(new EventB(), 2);
+ await consumer.DispatchEventAsync(new Unknown(), 2);
+
+ Assert.Equal(2, consumer.EventATriggered);
+ Assert.Equal(4, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public void Should_invoke_correct_event_and_return()
+ {
+ var consumer = new SyncFuncConsumer();
+
+ Assert.Equal(1, consumer.DispatchEvent(new EventA()));
+ Assert.Equal(1, consumer.DispatchEvent(new EventB()));
+ Assert.Equal(2, consumer.DispatchEvent(new EventB()));
+ Assert.Equal(9, consumer.DispatchEvent(new Unknown()));
+
+ Assert.Equal(1, consumer.EventATriggered);
+ Assert.Equal(2, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public void Should_invoke_correct_event_with_context_and_return()
+ {
+ var consumer = new SyncFuncConsumer();
+
+ Assert.Equal(11, consumer.DispatchEvent(new EventA(), 10));
+ Assert.Equal(11, consumer.DispatchEvent(new EventB(), 10));
+ Assert.Equal(12, consumer.DispatchEvent(new EventB(), 10));
+ Assert.Equal(13, consumer.DispatchEvent(new Unknown(), 10));
+
+ Assert.Equal(1, consumer.EventATriggered);
+ Assert.Equal(2, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public async Task Should_invoke_correct_event_and_return_synchronously()
+ {
+ var consumer = new AsyncFuncConsumer();
+
+ Assert.Equal(1, await consumer.DispatchEventAsync(new EventA()));
+ Assert.Equal(1, await consumer.DispatchEventAsync(new EventB()));
+ Assert.Equal(2, await consumer.DispatchEventAsync(new EventB()));
+ Assert.Equal(9, await consumer.DispatchEventAsync(new Unknown()));
+
+ Assert.Equal(1, consumer.EventATriggered);
+ Assert.Equal(2, consumer.EventBTriggered);
+ }
+
+ [Fact]
+ public async Task Should_invoke_correct_event_with_context_and_return_synchronously()
+ {
+ var consumer = new AsyncFuncConsumer();
+
+ Assert.Equal(11, await consumer.DispatchEventAsync(new EventA(), 10));
+ Assert.Equal(11, await consumer.DispatchEventAsync(new EventB(), 10));
+ Assert.Equal(12, await consumer.DispatchEventAsync(new EventB(), 10));
+ Assert.Equal(13, await consumer.DispatchEventAsync(new Unknown(), 10));
+
+ Assert.Equal(1, consumer.EventATriggered);
+ Assert.Equal(2, consumer.EventBTriggered);
+ }
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTest.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs
similarity index 94%
rename from src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTest.cs
rename to src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs
index 17eb65aaa..735ef5b94 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTest.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs
@@ -11,7 +11,7 @@ using Xunit;
namespace PinkParrot.Infrastructure
{
- public sealed class EnumExtensionsTest
+ public sealed class EnumExtensionsTests
{
[Fact]
public void Should_return_true_if_enum_is_valid()
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs
index 3e437edab..31d214887 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs
@@ -9,7 +9,9 @@
using System;
using System.Globalization;
using System.Linq;
+using Newtonsoft.Json;
using NodaTime;
+using PinkParrot.Infrastructure.Json;
using Xunit;
// ReSharper disable PossibleInvalidOperationException
@@ -28,6 +30,32 @@ namespace PinkParrot.Infrastructure
dynamicBag = bag;
}
+ [Fact]
+ public void Should_serialize_and_deserialize()
+ {
+ var time = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds());
+
+ bag.Set("Key1", time);
+ bag.Set("Key2", "MyString");
+ bag.Set("Key3", 123L);
+ bag.Set("Key4", true);
+ bag.Set("Key5", Guid.NewGuid());
+
+ var serializerSettings = new JsonSerializerSettings();
+
+ serializerSettings.Converters.Add(new PropertiesBagConverter());
+
+ var content = JsonConvert.SerializeObject(bag, serializerSettings);
+ var response = JsonConvert.DeserializeObject(content, serializerSettings);
+
+ foreach (var kvp in response.Properties.Take(4))
+ {
+ Assert.Equal(kvp.Value.RawValue, bag[kvp.Key].RawValue);
+ }
+
+ Assert.Equal(bag["Key5"].ToGuid(c), response["Key5"].ToGuid(c));
+ }
+
[Fact]
public void Should_return_false_when_renaming_unknown_property()
{
@@ -119,6 +147,18 @@ namespace PinkParrot.Infrastructure
Assert.Throws(() => bag.Set("Key", (byte)1));
}
+ [Fact]
+ public void Should_return_false_when_making_contains_check()
+ {
+ Assert.False(dynamicBag.Contains("Key"));
+ }
+
+ [Fact]
+ public void Should_provide_default_value_if_not_exists()
+ {
+ Assert.Equal(0, (int)dynamicBag.Key);
+ }
+
[Fact]
public void Should_convert_string_to_numbers()
{
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/AggregateCommand.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/AggregateCommand.cs
new file mode 100644
index 000000000..d9541efe1
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/AggregateCommand.cs
@@ -0,0 +1,17 @@
+// ==========================================================================
+// AggregateCommand.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public class AggregateCommand : ICommand
+ {
+ public Guid AggregateId { get; set; }
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandContext.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandContext.cs
new file mode 100644
index 000000000..1689921b1
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandContext.cs
@@ -0,0 +1,72 @@
+// ==========================================================================
+// CommandContext.cs
+// Green Parrot Framework
+// ==========================================================================
+// Copyright (c) Sebastian Stehle
+// All rights reserved.
+// ==========================================================================
+
+using System;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public sealed class CommandContext
+ {
+ private readonly IDomainObjectFactory factory;
+ private readonly IDomainObjectRepository repository;
+ private readonly ICommand command;
+ private Exception exception;
+ private bool isSucceeded;
+
+ public ICommand Command
+ {
+ get { return command; }
+ }
+
+ public IDomainObjectFactory Factory
+ {
+ get { return factory; }
+ }
+
+ public IDomainObjectRepository Repository
+ {
+ get { return repository; }
+ }
+
+ public bool IsHandled
+ {
+ get { return isSucceeded || exception != null; }
+ }
+
+ public bool IsSucceeded
+ {
+ get { return isSucceeded; }
+ }
+
+ public Exception Exception
+ {
+ get { return exception; }
+ }
+
+ public CommandContext(IDomainObjectFactory factory, IDomainObjectRepository repository, ICommand command)
+ {
+ Guard.NotNull(command, nameof(command));
+ Guard.NotNull(factory, nameof(factory));
+ Guard.NotNull(repository, nameof(repository));
+
+ this.command = command;
+ this.factory = factory;
+ this.repository = repository;
+ }
+
+ public void MarkSucceeded()
+ {
+ isSucceeded = true;
+ }
+
+ public void MarkFailed(Exception handlerException)
+ {
+ exception = handlerException;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandingExtensions.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandingExtensions.cs
new file mode 100644
index 000000000..c6d4f5f02
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandingExtensions.cs
@@ -0,0 +1,20 @@
+// ==========================================================================
+// CommandingExtensions.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public static class CommandingExtensions
+ {
+ public static T CreateNew(this IDomainObjectFactory factory, Guid id) where T : DomainObject
+ {
+ return (T)factory.CreateNew(typeof(T), id);
+ }
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommand.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommand.cs
new file mode 100644
index 000000000..465954f13
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommand.cs
@@ -0,0 +1,14 @@
+// ==========================================================================
+// ICommand.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public interface ICommand
+ {
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs
new file mode 100644
index 000000000..e486574fe
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs
@@ -0,0 +1,17 @@
+// ==========================================================================
+// ICommandBus.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System.Threading.Tasks;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public interface ICommandBus
+ {
+ Task PublishAsync(ICommand command);
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandHandler.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandHandler.cs
new file mode 100644
index 000000000..81f1e0226
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandHandler.cs
@@ -0,0 +1,17 @@
+// ==========================================================================
+// ICommandHandler.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System.Threading.Tasks;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public interface ICommandHandler
+ {
+ Task HandleAsync(CommandContext context);
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectFactory.cs
new file mode 100644
index 000000000..36acf7810
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectFactory.cs
@@ -0,0 +1,17 @@
+// ==========================================================================
+// IDomainObjectFactory.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public interface IDomainObjectFactory
+ {
+ IAggregate CreateNew(Type type, Guid id);
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectRepository.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectRepository.cs
new file mode 100644
index 000000000..b2b719f93
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectRepository.cs
@@ -0,0 +1,20 @@
+// ==========================================================================
+// IDomainObjectRepository.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Threading.Tasks;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public interface IDomainObjectRepository
+ {
+ Task GetByIdAsync(Guid id, int version = 0) where TDomainObject : class, IAggregate;
+
+ Task SaveAsync(IAggregate domainObject, Guid commitId);
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs
new file mode 100644
index 000000000..e01559228
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs
@@ -0,0 +1,59 @@
+// ==========================================================================
+// InMemoryCommandBus.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public sealed class InMemoryCommandBus : ICommandBus
+ {
+ private readonly IDomainObjectFactory factory;
+ private readonly IDomainObjectRepository repository;
+ private readonly IEnumerable handlers;
+
+ public InMemoryCommandBus(
+ IDomainObjectRepository repository,
+ IDomainObjectFactory factory,
+ IEnumerable handlers)
+ {
+ Guard.NotNull(factory, nameof(factory));
+ Guard.NotNull(handlers, nameof(handlers));
+ Guard.NotNull(repository, nameof(repository));
+
+ this.factory = factory;
+ this.handlers = handlers;
+ this.repository = repository;
+ }
+
+ public async Task PublishAsync(ICommand command)
+ {
+ Guard.NotNull(command, nameof(command));
+
+ var context = new CommandContext(factory, repository, command);
+
+ foreach (var handler in handlers)
+ {
+ try
+ {
+ var isHandled = await handler.HandleAsync(context);
+
+ if (isHandled)
+ {
+ context.MarkSucceeded();
+ }
+ }
+ catch (Exception e)
+ {
+ context.MarkFailed(e);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs
index ab4a02804..d10c1ec32 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs
@@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Infrastructure.CQRS
{
@@ -37,6 +38,8 @@ namespace PinkParrot.Infrastructure.CQRS
this.version = version;
}
+ protected abstract void ApplyEvent(IEvent @event);
+
protected void RaiseEvent(Envelope envelope, bool disableApply = false) where TEvent : class, IEvent
{
Guard.NotNull(envelope, nameof(envelope));
@@ -61,20 +64,9 @@ namespace PinkParrot.Infrastructure.CQRS
}
}
- protected void Apply(object @event)
- {
- }
-
- private void ApplyEvent(dynamic @event)
- {
- Apply(@event);
- version++;
- }
-
void IAggregate.ApplyEvent(IEvent @event)
{
- Apply(@event as dynamic);
- version++;
+ ApplyEvent(@event); version++;
}
void IAggregate.ClearUncommittedEvents()
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeFactory.cs
index fcfa1d203..dbf64ce45 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeFactory.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeFactory.cs
@@ -8,6 +8,7 @@
using System;
using NodaTime;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Infrastructure.CQRS
{
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IEvent.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEvent.cs
similarity index 88%
rename from src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IEvent.cs
rename to src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEvent.cs
index 6ee3da7ea..7d9370c77 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IEvent.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEvent.cs
@@ -5,7 +5,7 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
-namespace PinkParrot.Infrastructure.CQRS
+namespace PinkParrot.Infrastructure.CQRS.Events
{
public interface IEvent
{
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEventConsumer.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEventConsumer.cs
new file mode 100644
index 000000000..f7a243d13
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEventConsumer.cs
@@ -0,0 +1,15 @@
+// ==========================================================================
+// IEventConsumer.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+namespace PinkParrot.Infrastructure.CQRS.Events
+{
+ public interface IEventConsumer
+ {
+ void On(Envelope @event);
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs
index f640513c4..8d71829ee 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs
@@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
+using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Infrastructure.CQRS
{
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs
new file mode 100644
index 000000000..a01cc87be
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs
@@ -0,0 +1,45 @@
+// ==========================================================================
+// ActionDispatcher.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ public sealed class ActionContextDispatcher
+ {
+ private static readonly Dictionary> Handlers;
+
+ static ActionContextDispatcher()
+ {
+ Handlers =
+ typeof(TTarget)
+ .GetMethods()
+ .Where(Helper.HasRightName)
+ .Where(Helper.HasRightParameters)
+ .Select(ActionContextDispatcherFactory.CreateActionHandler)
+ .ToDictionary(h => h.Item1, h => h.Item2);
+ }
+
+ public static bool Dispatch(TTarget target, TIn input, TContext context)
+ {
+ Action handler;
+
+ if (!Handlers.TryGetValue(input.GetType(), out handler))
+ {
+ return false;
+ }
+
+ handler(target, input, context);
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs
new file mode 100644
index 000000000..e9d7aa153
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs
@@ -0,0 +1,39 @@
+// ==========================================================================
+// ActionHandlerFactory.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ internal class ActionContextDispatcherFactory
+ {
+ public static Tuple> CreateActionHandler(MethodInfo methodInfo)
+ {
+ var inputType = methodInfo.GetParameters()[0].ParameterType;
+
+ var factoryMethod =
+ typeof(ActionContextDispatcherFactory)
+ .GetMethod("Factory", BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(typeof(TTarget), inputType, typeof(TContext));
+
+ var handler = factoryMethod.Invoke(null, new object[] { methodInfo });
+
+ return new Tuple>(inputType, (Action)handler);
+ }
+
+ private static Action Factory(MethodInfo methodInfo)
+ {
+ var type = typeof(Action);
+
+ var handler = (Action)methodInfo.CreateDelegate(type);
+
+ return (target, input, context) => handler(target, (TIn)input, context);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs
new file mode 100644
index 000000000..d0b1ede37
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs
@@ -0,0 +1,45 @@
+// ==========================================================================
+// ActionDispatcher.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ public sealed class ActionDispatcher
+ {
+ private static readonly Dictionary> Handlers;
+
+ static ActionDispatcher()
+ {
+ Handlers =
+ typeof(TTarget)
+ .GetMethods()
+ .Where(Helper.HasRightName)
+ .Where(Helper.HasRightParameters)
+ .Select(ActionDispatcherFactory.CreateActionHandler)
+ .ToDictionary(h => h.Item1, h => h.Item2);
+ }
+
+ public static bool Dispatch(TTarget target, TIn item)
+ {
+ Action handler;
+
+ if (!Handlers.TryGetValue(item.GetType(), out handler))
+ {
+ return false;
+ }
+
+ handler(target, item);
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs
new file mode 100644
index 000000000..1222e4419
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs
@@ -0,0 +1,39 @@
+// ==========================================================================
+// ActionHandlerFactory.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ internal class ActionDispatcherFactory
+ {
+ public static Tuple> CreateActionHandler(MethodInfo methodInfo)
+ {
+ var inputType = methodInfo.GetParameters()[0].ParameterType;
+
+ var factoryMethod =
+ typeof(ActionDispatcherFactory)
+ .GetMethod("Factory", BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(typeof(T), inputType);
+
+ var handler = factoryMethod.Invoke(null, new object[] { methodInfo });
+
+ return new Tuple>(inputType, (Action)handler);
+ }
+
+ private static Action Factory(MethodInfo methodInfo)
+ {
+ var type = typeof(Action);
+
+ var handler = (Action)methodInfo.CreateDelegate(type);
+
+ return (target, input) => handler(target, (TIn)input);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/DispatchExtensions.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/DispatchExtensions.cs
new file mode 100644
index 000000000..d97348b84
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/DispatchExtensions.cs
@@ -0,0 +1,81 @@
+// ==========================================================================
+// DispatchExtensions.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System.Threading.Tasks;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ public static class DispatchExtensions
+ {
+ public static bool DispatchAction(this TTarget target, TIn input)
+ {
+ return ActionDispatcher.Dispatch(target, input);
+ }
+
+ public static bool DispatchAction(this TTarget target, TIn input, TContext context)
+ {
+ return ActionContextDispatcher.Dispatch(target, input, context);
+ }
+
+ public static async Task DispatchActionAsync(this TTarget target, TIn input)
+ {
+ var task = FuncDispatcher.Dispatch(target, input);
+
+ if (task == null)
+ {
+ return false;
+ }
+
+ await task;
+
+ return true;
+ }
+
+ public static async Task DispatchActionAsync(this TTarget target, TIn input, TContext context)
+ {
+ var task = FuncContextDispatcher.Dispatch(target, input, context);
+
+ if (task == null)
+ {
+ return false;
+ }
+
+ await task;
+
+ return true;
+ }
+
+ public static TOut DispatchFunc(this TTarget target, TIn input, TOut fallback)
+ {
+ var result = FuncDispatcher.Dispatch(target, input);
+
+ return Equals(result, default(TOut)) ? fallback : result;
+ }
+
+ public static TOut DispatchFunc(this TTarget target, TIn input, TContext context, TOut fallback)
+ {
+ var result = FuncContextDispatcher.Dispatch(target, input, context);
+
+ return Equals(result, default(TOut)) ? fallback : result;
+ }
+
+ public static Task DispatchFuncAsync(this TTarget target, TIn input, TOut fallback)
+ {
+ var result = FuncDispatcher>.Dispatch(target, input);
+
+ return result ?? Task.FromResult(fallback);
+ }
+
+ public static Task DispatchFuncAsync(this TTarget target, TIn input, TContext context, TOut fallback)
+ {
+ var result = FuncContextDispatcher>.Dispatch(target, input, context);
+
+ return result ?? Task.FromResult(fallback);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs
new file mode 100644
index 000000000..e166d71da
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs
@@ -0,0 +1,44 @@
+// ==========================================================================
+// FuncDispatcher.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ public sealed class FuncContextDispatcher
+ {
+ private static readonly Dictionary> Handlers;
+
+ static FuncContextDispatcher()
+ {
+ Handlers =
+ typeof(TTarget)
+ .GetMethods()
+ .Where(Helper.HasRightName)
+ .Where(Helper.HasRightParameters)
+ .Where(Helper.HasRightReturnType)
+ .Select(FuncContextDispatcherFactory.CreateFuncHandler)
+ .ToDictionary(h => h.Item1, h => h.Item2);
+ }
+
+ public static TOut Dispatch(TTarget target, TIn item, TContext context)
+ {
+ Func handler;
+
+ if (Handlers.TryGetValue(item.GetType(), out handler))
+ {
+ return handler(target, item, context);
+ }
+
+ return default(TOut);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs
new file mode 100644
index 000000000..0849d245e
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs
@@ -0,0 +1,39 @@
+// ==========================================================================
+// HandlerFactory.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ internal static class FuncContextDispatcherFactory
+ {
+ public static Tuple> CreateFuncHandler(MethodInfo methodInfo)
+ {
+ var inputType = methodInfo.GetParameters()[0].ParameterType;
+
+ var factoryMethod =
+ typeof(FuncContextDispatcherFactory)
+ .GetMethod("Factory", BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(typeof(TTarget), inputType, typeof(TContext), methodInfo.ReturnType);
+
+ var handler = factoryMethod.Invoke(null, new object[] { methodInfo });
+
+ return new Tuple>(inputType, (Func)handler);
+ }
+
+ private static Func Factory(MethodInfo methodInfo)
+ {
+ var type = typeof(Func);
+
+ var handler = (Func)methodInfo.CreateDelegate(type);
+
+ return (target, input, context) => handler(target, (TIn)input, context);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs
new file mode 100644
index 000000000..488197286
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs
@@ -0,0 +1,44 @@
+// ==========================================================================
+// FuncDispatcher.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ public sealed class FuncDispatcher
+ {
+ private static readonly Dictionary> Handlers;
+
+ static FuncDispatcher()
+ {
+ Handlers =
+ typeof(TTarget)
+ .GetMethods()
+ .Where(Helper.HasRightName)
+ .Where(Helper.HasRightParameters)
+ .Where(Helper.HasRightReturnType)
+ .Select(FuncDispatcherFactory.CreateFuncHandler)
+ .ToDictionary(h => h.Item1, h => h.Item2);
+ }
+
+ public static TOut Dispatch(TTarget target, TIn item)
+ {
+ Func handler;
+
+ if (Handlers.TryGetValue(item.GetType(), out handler))
+ {
+ return handler(target, item);
+ }
+
+ return default(TOut);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs
new file mode 100644
index 000000000..797816c4c
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs
@@ -0,0 +1,39 @@
+// ==========================================================================
+// HandlerFactory.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ internal static class FuncDispatcherFactory
+ {
+ public static Tuple> CreateFuncHandler(MethodInfo methodInfo)
+ {
+ var inputType = methodInfo.GetParameters()[0].ParameterType;
+
+ var factoryMethod =
+ typeof(FuncDispatcherFactory)
+ .GetMethod("Factory", BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(typeof(TTarget), inputType, methodInfo.ReturnType);
+
+ var handler = factoryMethod.Invoke(null, new object[] { methodInfo });
+
+ return new Tuple>(inputType, (Func)handler);
+ }
+
+ private static Func Factory(MethodInfo methodInfo)
+ {
+ var type = typeof(Func);
+
+ var handler = (Func)methodInfo.CreateDelegate(type);
+
+ return (target, input) => handler(target, (TIn)input);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/Helper.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/Helper.cs
new file mode 100644
index 000000000..cc97c9a06
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/Helper.cs
@@ -0,0 +1,39 @@
+// ==========================================================================
+// Helper.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System.Reflection;
+
+namespace PinkParrot.Infrastructure.Dispatching
+{
+ internal static class Helper
+ {
+ public static bool HasRightName(MethodInfo method)
+ {
+ return method.Name == "On";
+ }
+
+ public static bool HasRightReturnType(MethodInfo method)
+ {
+ return method.ReturnType == typeof(TOut);
+ }
+
+ public static bool HasRightParameters(MethodInfo method)
+ {
+ var parameters = method.GetParameters();
+
+ return parameters.Length == 1 && typeof(TIn).IsAssignableFrom(parameters[0].ParameterType);
+ }
+
+ public static bool HasRightParameters(MethodInfo method)
+ {
+ var parameters = method.GetParameters();
+
+ return parameters.Length == 2 && typeof(TIn).IsAssignableFrom(parameters[0].ParameterType) && parameters[1].ParameterType == typeof(TContext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs
new file mode 100644
index 000000000..c1aad3861
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace PinkParrot.Infrastructure.CQRS.Commands
+{
+ public interface ICommandContext
+ {
+ ICommand Command { get; }
+ Exception Exception { get; }
+ IDomainObjectFactory Factory { get; }
+ bool IsFailed { get; }
+ bool IsHandled { get; }
+ bool IsSucceeded { get; }
+ IDomainObjectRepository Repository { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/JsonPropertiesBagConverter.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/JsonPropertiesBagConverter.cs
new file mode 100644
index 000000000..af3b5a364
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/JsonPropertiesBagConverter.cs
@@ -0,0 +1,76 @@
+// ==========================================================================
+// JsonPropertiesBagConverter.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using Newtonsoft.Json;
+using NodaTime;
+using NodaTime.Extensions;
+
+namespace PinkParrot.Infrastructure.Json
+{
+ public sealed class PropertiesBagConverter : JsonConverter
+ {
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(PropertiesBag);
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var properties = new PropertiesBag();
+
+ while (reader.Read())
+ {
+ if (reader.TokenType != JsonToken.PropertyName)
+ {
+ break;
+ }
+
+ var key = reader.Value.ToString();
+
+ reader.Read();
+
+ var value = reader.Value;
+
+ if (value is DateTime)
+ {
+ properties.Set(key, ((DateTime)value).ToInstant());
+ }
+ else
+ {
+ properties.Set(key, value);
+ }
+ }
+
+ return properties;
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var properties = (PropertiesBag)value;
+
+ writer.WriteStartObject();
+
+ foreach (var kvp in properties.Properties)
+ {
+ writer.WritePropertyName(kvp.Key);
+
+ if (kvp.Value.RawValue is Instant)
+ {
+ writer.WriteValue(kvp.Value.ToString());
+ }
+ else
+ {
+ writer.WriteValue(kvp.Value.RawValue);
+ }
+ }
+
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertiesBag.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertiesBag.cs
index 612803f97..72d0acd2a 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertiesBag.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertiesBag.cs
@@ -14,6 +14,7 @@ namespace PinkParrot.Infrastructure
{
public class PropertiesBag : DynamicObject
{
+ private static readonly PropertyValue FallbackValue = new PropertyValue(null);
private readonly Dictionary internalDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
public int Count
@@ -37,7 +38,7 @@ namespace PinkParrot.Infrastructure
{
Guard.NotNullOrEmpty(propertyName, nameof(propertyName));
- return internalDictionary[propertyName];
+ return internalDictionary.GetOrDefault(propertyName) ?? FallbackValue;
}
}
@@ -48,7 +49,9 @@ namespace PinkParrot.Infrastructure
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
- return internalDictionary.TryGetValueAsObject(binder.Name, out result);
+ result = this[binder.Name];
+
+ return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json
index 51889eb58..e07b8b190 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json
@@ -1,8 +1,11 @@
-{
+{
"version": "1.0.0-*",
"dependencies": {
"NodaTime": "2.0.0-alpha20160729",
- "NETStandard.Library": "1.6.0"
+ "NETStandard.Library": "1.6.0",
+ "System.Reflection.TypeExtensions": "4.1.0",
+ "System.Linq": "4.1.0",
+ "Newtonsoft.Json": "9.0.1"
},
"frameworks": {
"netcoreapp1.0": {
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
index 1a334abd6..dd9b1937c 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class AddModelField
+ public class AddModelField : AggregateCommand
{
- public Guid AggregateId;
-
public string FieldType;
public string FieldName;
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs
index cf7109f6d..6be8f93b7 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class CreateModelSchema
+ public class CreateModelSchema : AggregateCommand
{
- public Guid AggregateId;
-
public string Name;
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
index 434cd385f..fb650e360 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class DeleteModelField
+ public class DeleteModelField : AggregateCommand
{
- public Guid AggregateId;
-
- public Guid FieldId;
+ public long FieldId;
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs
index dd737f813..5a35849bb 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs
@@ -6,12 +6,11 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class DeleteModelSchema
+ public class DeleteModelSchema : AggregateCommand
{
- public Guid AggregateId;
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs
index 3a115a3b9..d07d1a0fe 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class DisableModelField
+ public class DisableModelField : AggregateCommand
{
- public Guid AggregateId;
-
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
index 5d99b1b82..fd28e8e32 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class EnableModelField
+ public class EnableModelField : AggregateCommand
{
- public Guid AggregateId;
-
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
index dc4c9ffe8..2b6640ce0 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class HideModelField
+ public class HideModelField : AggregateCommand
{
- public Guid AggregateId;
-
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
index f65f8dd9e..9cef4a2cd 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
@@ -6,14 +6,12 @@
// All rights reserved.
// ==========================================================================
-using System;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class ShowModelField
+ public class ShowModelField : AggregateCommand
{
- public Guid AggregateId;
-
- public Guid FieldId;
+ public long FieldId;
}
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
index 68c604dfe..387c34335 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
@@ -6,16 +6,14 @@
// All rights reserved.
// ==========================================================================
-using System;
using PinkParrot.Infrastructure;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class UpdateModelField
+ public class UpdateModelField : AggregateCommand
{
- public Guid AggregateId;
-
- public Guid FieldId;
+ public long FieldId;
public PropertiesBag Settings;
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs
index 75b636e49..3aada1f07 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs
@@ -6,15 +6,13 @@
// All rights reserved.
// ==========================================================================
-using System;
using PinkParrot.Infrastructure;
+using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands
{
- public class UpdateModelSchema
+ public class UpdateModelSchema : AggregateCommand
{
- public Guid AggregateId;
-
public string NewName;
public PropertiesBag Settings { get; set; }
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs
new file mode 100644
index 000000000..dd29aa781
--- /dev/null
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs
@@ -0,0 +1,87 @@
+// ==========================================================================
+// ModelSchemaCommandHandler.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Threading.Tasks;
+using PinkParrot.Infrastructure.CQRS.Commands;
+using PinkParrot.Infrastructure.Dispatching;
+using PinkParrot.Write.Schema.Commands;
+
+namespace PinkParrot.Write.Schema
+{
+ public class ModelSchemaCommandHandler : ICommandHandler
+ {
+ public Task HandleAsync(CommandContext context)
+ {
+ return this.DispatchActionAsync(context.Command, context);
+ }
+
+ protected Task On(AddModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.AddField(command));
+ }
+
+ protected Task On(DeleteModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.DeleteField(command));
+ }
+
+ protected Task On(DeleteModelSchema command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.Delete(command));
+ }
+
+ protected Task On(DisableModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.DisableField(command));
+ }
+
+ protected Task On(EnableModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.EnableField(command));
+ }
+
+ protected Task On(HideModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.HideField(command));
+ }
+
+ protected Task On(ShowModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.ShowField(command));
+ }
+
+ protected Task On(UpdateModelField command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.UpdateField(command));
+ }
+
+ protected Task On(UpdateModelSchema command, CommandContext context)
+ {
+ return Update(command, context, schema => schema.Update(command));
+ }
+
+ protected Task On(CreateModelSchema command, CommandContext context)
+ {
+ var schema = context.Factory.CreateNew(command.AggregateId);
+
+ schema.Create(command);
+
+ return context.Repository.SaveAsync(schema, Guid.NewGuid());
+ }
+
+ private async Task Update(AggregateCommand command, CommandContext context, Action updater)
+ {
+ var schema = await context.Repository.GetByIdAsync(command.AggregateId);
+
+ updater(schema);
+
+ await context.Repository.SaveAsync(schema, Guid.NewGuid());
+ }
+ }
+}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs
index d7a3171e6..2b7b1e882 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs
@@ -10,6 +10,8 @@ using System;
using PinkParrot.Core.Schema;
using PinkParrot.Events.Schema;
using PinkParrot.Infrastructure.CQRS;
+using PinkParrot.Infrastructure.CQRS.Events;
+using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Write.Schema.Commands;
namespace PinkParrot.Write.Schema
@@ -18,14 +20,9 @@ namespace PinkParrot.Write.Schema
{
private readonly ModelFieldFactory fieldFactory;
private bool isDeleted;
+ private long totalFields;
private ModelSchema schema;
- public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory fieldFactory)
- : base(id, version)
- {
- this.fieldFactory = fieldFactory;
- }
-
public ModelSchema Schema
{
get { return schema; }
@@ -36,14 +33,22 @@ namespace PinkParrot.Write.Schema
get { return isDeleted; }
}
- protected void Apply(ModelSchemaCreated @event)
+ public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory fieldFactory)
+ : base(id, version)
{
- schema = ModelSchema.Create(@event.Name);
+ this.fieldFactory = fieldFactory;
}
protected void Apply(ModelFieldAdded @event)
{
schema = schema.AddField(@event.FieldId, @event.FieldType, @event.FieldName, fieldFactory);
+
+ totalFields++;
+ }
+
+ protected void Apply(ModelSchemaCreated @event)
+ {
+ schema = ModelSchema.Create(@event.Name);
}
protected void Apply(ModelFieldUpdated @event)
@@ -86,23 +91,24 @@ namespace PinkParrot.Write.Schema
isDeleted = false;
}
- public void Create(CreateModelSchema command)
+ public void AddField(AddModelField command)
{
- VerifyNotCreated();
+ VerifyCreatedAndNotDeleted();
- schema = ModelSchema.Create(command.Name);
+ var id = ++totalFields;
+
+ schema = schema.AddField(id, command.FieldType, command.FieldName, fieldFactory);
- RaiseEvent(new ModelSchemaCreated {Name = command.Name}, true);
+ RaiseEvent(new ModelFieldAdded { FieldId = id, FieldType = command.FieldType, FieldName = command.FieldName }, true);
}
- public void AddField(Guid id, AddModelField command)
+ public void Create(CreateModelSchema command)
{
- VerifyCreatedAndNotDeleted();
+ VerifyNotCreated();
- schema = schema.AddField(id, command.FieldType, command.FieldName, fieldFactory);
+ schema = ModelSchema.Create(command.Name);
- RaiseEvent(
- new ModelFieldAdded {FieldId = id, FieldType = command.FieldType, FieldName = command.FieldName}, true);
+ RaiseEvent(new ModelSchemaCreated { Name = command.Name }, true);
}
public void Update(UpdateModelSchema command)
@@ -111,7 +117,7 @@ namespace PinkParrot.Write.Schema
schema = schema.Update(schema.Metadata.Configure(command.NewName, command.Settings));
- RaiseEvent(new ModelSchemaUpdated {NewName = command.NewName, Settings = command.Settings}, true);
+ RaiseEvent(new ModelSchemaUpdated { NewName = command.NewName, Settings = command.Settings }, true);
}
public void UpdateField(UpdateModelField command)
@@ -120,7 +126,7 @@ namespace PinkParrot.Write.Schema
schema = schema.SetField(command.FieldId, command.Settings);
- RaiseEvent(new ModelFieldUpdated {FieldId = command.FieldId, Settings = command.Settings}, true);
+ RaiseEvent(new ModelFieldUpdated { FieldId = command.FieldId, Settings = command.Settings }, true);
}
public void HideField(HideModelField command)
@@ -129,16 +135,16 @@ namespace PinkParrot.Write.Schema
schema = schema.HideField(command.FieldId);
- RaiseEvent(new ModelFieldHidden {FieldId = command.FieldId}, true);
+ RaiseEvent(new ModelFieldHidden { FieldId = command.FieldId }, true);
}
- public void ShowField(HideModelField command)
+ public void ShowField(ShowModelField command)
{
VerifyCreatedAndNotDeleted();
schema = schema.ShowField(command.FieldId);
- RaiseEvent(new ModelFieldShown {FieldId = command.FieldId}, true);
+ RaiseEvent(new ModelFieldShown { FieldId = command.FieldId }, true);
}
public void DisableField(DisableModelField command)
@@ -147,7 +153,7 @@ namespace PinkParrot.Write.Schema
schema = schema.DisableField(command.FieldId);
- RaiseEvent(new ModelFieldDisabled {FieldId = command.FieldId}, true);
+ RaiseEvent(new ModelFieldDisabled { FieldId = command.FieldId }, true);
}
public void EnableField(EnableModelField command)
@@ -156,7 +162,7 @@ namespace PinkParrot.Write.Schema
schema = schema.EnableField(command.FieldId);
- RaiseEvent(new ModelFieldEnabled {FieldId = command.FieldId}, true);
+ RaiseEvent(new ModelFieldEnabled { FieldId = command.FieldId }, true);
}
public void Delete(DeleteModelSchema command)
@@ -174,7 +180,7 @@ namespace PinkParrot.Write.Schema
schema = schema.DeleteField(command.FieldId);
- RaiseEvent(new ModelFieldDeleted {FieldId = command.FieldId}, true);
+ RaiseEvent(new ModelFieldDeleted { FieldId = command.FieldId }, true);
}
private void VerifyNotCreated()
@@ -192,5 +198,10 @@ namespace PinkParrot.Write.Schema
throw new InvalidOperationException("Schema has already been deleted or not created yet.");
}
}
+
+ protected override void ApplyEvent(IEvent @event)
+ {
+ this.DispatchAction(@event);
+ }
}
}
\ No newline at end of file