Browse Source

Some progress

pull/1/head
Sebastian Stehle 9 years ago
parent
commit
86707e75d8
  1. 6
      PinkParrot.sln.DotSettings
  2. 26
      src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
  3. 187
      src/PinkParrot/Project_Readme.html
  4. 24
      src/PinkParrot/project.json
  5. 33
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs
  6. 4
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs
  7. 26
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
  8. 25
      src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs
  9. 7
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs
  10. 5
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs
  11. 5
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs
  12. 5
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs
  13. 5
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs
  14. 5
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs
  15. 5
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs
  16. 2
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs
  17. 2
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs
  18. 2
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs
  19. 0
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs
  20. 284
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs
  21. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs
  22. 40
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs
  23. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/AggregateCommand.cs
  24. 72
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandContext.cs
  25. 20
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandingExtensions.cs
  26. 14
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommand.cs
  27. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs
  28. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/ICommandHandler.cs
  29. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectFactory.cs
  30. 20
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IDomainObjectRepository.cs
  31. 59
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs
  32. 16
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs
  33. 1
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeFactory.cs
  34. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEvent.cs
  35. 15
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEventConsumer.cs
  36. 1
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs
  37. 45
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs
  38. 39
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs
  39. 45
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs
  40. 39
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs
  41. 81
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/DispatchExtensions.cs
  42. 44
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs
  43. 39
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs
  44. 44
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs
  45. 39
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs
  46. 39
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/Helper.cs
  47. 15
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs
  48. 76
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/JsonPropertiesBagConverter.cs
  49. 7
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertiesBag.cs
  50. 7
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json
  51. 6
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
  52. 6
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs
  53. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
  54. 5
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs
  55. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs
  56. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
  57. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
  58. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
  59. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
  60. 6
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs
  61. 87
      src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs
  62. 61
      src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs

6
PinkParrot.sln.DotSettings

@ -33,6 +33,9 @@
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue"></s:String> <s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue"></s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_MULTIPLICATIVE_OP/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_MULTIPLICATIVE_OP/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHING_EMPTY_BRACES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/JavaScriptCodeFormatting/QUOTE_STYLE/@EntryValue">SingleQuoted</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/JavaScriptCodeFormatting/QUOTE_STYLE/@EntryValue">SingleQuoted</s:String>
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">==========================================================================&#xD; <s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">==========================================================================&#xD;
$FILENAME$&#xD; $FILENAME$&#xD;
@ -83,4 +86,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/CodeStyle/TypeScriptCodeStyle/ExplicitPublicModifier/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/TypeScriptCodeStyle/ExplicitPublicModifier/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

26
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()
{
}
}
}

187
src/PinkParrot/Project_Readme.html

@ -1,187 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Welcome to ASP.NET Core</title>
<style>
html {
background: #f1f1f1;
height: 100%;
}
body {
background: #fff;
color: #505050;
font: 14px 'Segoe UI', tahoma, arial, helvetica, sans-serif;
margin: 1%;
min-height: 95.5%;
border: 1px solid silver;
position: relative;
}
#header {
padding: 0;
}
#header h1 {
font-size: 44px;
font-weight: normal;
margin: 0;
padding: 10px 30px 10px 30px;
}
#header span {
margin: 0;
padding: 0 30px;
display: block;
}
#header p {
font-size: 20px;
color: #fff;
background: #007acc;
padding: 0 30px;
line-height: 50px;
margin-top: 25px;
}
#header p a {
color: #fff;
text-decoration: underline;
font-weight: bold;
padding-right: 35px;
background: no-repeat right bottom url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAWCAMAAAAcqPc3AAAANlBMVEUAAAAAeswfitI9mthXp91us+KCvuaTx+mjz+2x1u+83PLH4vTR5/ba7Pjj8Pns9fv1+v3////wy3dWAAAAAXRSTlMAQObYZgAAAHxJREFUeNp9kVcSwCAIRMHUYoH7XzaxOxJ9P8oyQ1uIqNPwh3s2aLmIM2YtqrLcQIeQEylhuCeUOlhgve5yoBCfWmlnlgkN4H8ykbpaE7gR03AbUHiwoOxUH9Xp+ubd41p1HF3mBPrfC87BHeTdaB3ceeKL9HGpcvX9zu6+DdMWT9KQPvYAAAAASUVORK5CYII=);
}
#main {
padding: 5px 30px;
clear: both;
}
.section {
width: 21.7%;
float: left;
margin: 0 0 0 4%;
}
.section h2 {
font-size: 13px;
text-transform: uppercase;
margin: 0;
border-bottom: 1px solid silver;
padding-bottom: 12px;
margin-bottom: 8px;
}
.section.first {
margin-left: 0;
}
.section.first h2 {
font-size: 24px;
text-transform: none;
margin-bottom: 25px;
border: none;
}
.section.first li {
border-top: 1px solid silver;
padding: 8px 0;
}
.section.last {
margin-right: 0;
}
ul {
list-style: none;
padding: 0;
margin: 0;
line-height: 20px;
}
li {
padding: 4px 0;
}
a {
color: #267cb2;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
#footer {
clear: both;
padding-top: 50px;
}
#footer p {
position: absolute;
bottom: 10px;
}
</style>
</head>
<body>
<div id="header">
<h1>Welcome to ASP.NET Core</h1>
<span>
We've made some big updates in this release, so it’s <b>important</b> that you spend
a few minutes to learn what’s new.
</span>
<p>You've created a new ASP.NET Core project. <a href="http://go.microsoft.com/fwlink/?LinkId=518016">Learn what's new</a></p>
</div>
<div id="main">
<div class="section first">
<h2>This application consists of:</h2>
<ul>
<li>Sample pages using ASP.NET Core MVC</li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518004">Bower</a> for managing client-side libraries</li>
<li>Theming using <a href="http://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a></li>
</ul>
</div>
<div class="section">
<h2>How to</h2>
<ul>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398600">Add a Controller and View</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699562">Add an appsetting in config and access it in app.</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699315">Manage User Secrets using Secret Manager.</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699316">Use logging to log a message.</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699317">Add packages using NuGet.</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699318">Add client packages using Bower.</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699319">Target development, staging or production environment.</a></li>
</ul>
</div>
<div class="section">
<h2>Overview</h2>
<ul>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518008">Conceptual overview of what is ASP.NET Core</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=699320">Fundamentals of ASP.NET Core such as Startup and middleware.</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=398602">Working with Data</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkId=398603">Security</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699321">Client side development</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699322">Develop on different platforms</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=699323">Read more on the documentation site</a></li>
</ul>
</div>
<div class="section last">
<h2>Run & Deploy</h2>
<ul>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517851">Run your app</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517853">Run tools such as EF migrations and more</a></li>
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398609">Publish to Microsoft Azure Web Apps</a></li>
</ul>
</div>
<div id="footer">
<p>We would love to hear your <a href="http://go.microsoft.com/fwlink/?LinkId=518015">feedback</a></p>
</div>
</div>
</body>
</html>

24
src/PinkParrot/project.json

@ -1,14 +1,28 @@
{ {
"dependencies": { "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": { "Microsoft.NETCore.App": {
"version": "1.0.0", "version": "1.0.0",
"type": "platform" "type": "platform"
}, },
"Microsoft.AspNetCore.Diagnostics": "1.0.0", "PinkParrot.Core": "1.0.0-*",
"PinkParrot.Events": "1.0.0-*",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "PinkParrot.Infrastructure": "1.0.0-*",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "PinkParrot.Write": "1.0.0-*"
"Microsoft.Extensions.Logging.Console": "1.0.0"
}, },
"tools": { "tools": {

33
src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs

@ -6,9 +6,7 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.Tasks; using PinkParrot.Infrastructure.Tasks;
@ -19,7 +17,7 @@ namespace PinkParrot.Core.Schema
{ {
public abstract class ModelField public abstract class ModelField
{ {
private readonly Guid id; private readonly long id;
private string name; private string name;
private string hint; private string hint;
private string displayName; private string displayName;
@ -27,7 +25,7 @@ namespace PinkParrot.Core.Schema
private bool isDisabled; private bool isDisabled;
private bool isHidden; private bool isHidden;
public Guid Id public long Id
{ {
get { return id; } get { return id; }
} }
@ -62,9 +60,9 @@ namespace PinkParrot.Core.Schema
get { return isDisabled; } 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)); Guard.ValidSlug(name, nameof(name));
this.id = id; this.id = id;
@ -72,13 +70,13 @@ namespace PinkParrot.Core.Schema
this.name = name; this.name = name;
} }
public ModelField Configure(PropertiesBag settings, ICollection<string> errors) public ModelField Configure(dynamic settings, ICollection<string> errors)
{ {
var clone = Clone(); var clone = Clone();
if (settings.Contains("Name")) if (settings.Contains("Name"))
{ {
clone.name = settings["Name"].ToString(); clone.name = settings.Name;
if (!clone.name.IsSlug()) 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")) if (settings.Contains("IsRequired"))
{ {
try clone.isRequired = settings.IsRequired;
{
clone.isRequired = settings["IsRequired"].ToBoolean(CultureInfo.InvariantCulture);
}
catch (InvalidCastException)
{
errors.Add("IsRequired is not a valid boolean");
}
} }
clone.ConfigureCore(settings, errors); clone.ConfigureCore(settings, errors);
@ -113,7 +104,7 @@ namespace PinkParrot.Core.Schema
return clone; return clone;
} }
protected virtual void ConfigureCore(PropertiesBag settings, ICollection<string> errors) protected virtual void ConfigureCore(dynamic settings, ICollection<string> errors)
{ {
} }

4
src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs

@ -6,13 +6,11 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
namespace PinkParrot.Core.Schema namespace PinkParrot.Core.Schema
{ {
public class ModelFieldFactory 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); return new NumberField(id, fieldName);
} }

26
src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs

@ -18,10 +18,10 @@ namespace PinkParrot.Core.Schema
public sealed class ModelSchema public sealed class ModelSchema
{ {
private readonly ModelSchemaMetadata metadata; private readonly ModelSchemaMetadata metadata;
private readonly ImmutableDictionary<Guid, ModelField> fields; private readonly ImmutableDictionary<long, ModelField> fields;
private readonly Dictionary<string, ModelField> fieldsByName; private readonly Dictionary<string, ModelField> fieldsByName;
public ModelSchema(ModelSchemaMetadata metadata, ImmutableDictionary<Guid, ModelField> fields) public ModelSchema(ModelSchemaMetadata metadata, ImmutableDictionary<long, ModelField> fields)
{ {
Guard.NotNull(fields, nameof(fields)); Guard.NotNull(fields, nameof(fields));
Guard.NotNull(metadata, nameof(metadata)); 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."); throw new DomainValidationException("Cannot create the schema.", $"'{name}' is not a valid slug.");
} }
return new ModelSchema(new ModelSchemaMetadata(name), ImmutableDictionary<Guid, ModelField>.Empty); return new ModelSchema(new ModelSchemaMetadata(name), ImmutableDictionary<long, ModelField>.Empty);
} }
public IReadOnlyDictionary<Guid, ModelField> Fields public IReadOnlyDictionary<long, ModelField> Fields
{ {
get { return fields; } get { return fields; }
} }
@ -60,14 +60,14 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(newMetadata, fields); 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); var field = factory.CreateField(id, type, fieldName);
return SetField(field); return SetField(field);
} }
public ModelSchema SetField(Guid fieldId, PropertiesBag settings) public ModelSchema SetField(long fieldId, PropertiesBag settings)
{ {
Guard.NotNull(settings, nameof(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()); return UpdateField(fieldId, field => field.Disable());
} }
public ModelSchema EnableField(Guid fieldId) public ModelSchema EnableField(long fieldId)
{ {
return UpdateField(fieldId, field => field.Enable()); return UpdateField(fieldId, field => field.Enable());
} }
public ModelSchema HideField(Guid fieldId) public ModelSchema HideField(long fieldId)
{ {
return UpdateField(fieldId, field => field.Show()); return UpdateField(fieldId, field => field.Show());
} }
public ModelSchema ShowField(Guid fieldId) public ModelSchema ShowField(long fieldId)
{ {
return UpdateField(fieldId, field => field.Show()); return UpdateField(fieldId, field => field.Show());
} }
@ -118,10 +118,8 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(metadata, fields.SetItem(field.Id, field)); 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)) if (!fields.ContainsKey(fieldId))
{ {
throw new DomainValidationException($"A field with id {fieldId} does not exist."); 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)); return new ModelSchema(metadata, fields.Remove(fieldId));
} }
private ModelSchema UpdateField(Guid fieldId, Func<ModelField, ModelField> updater) private ModelSchema UpdateField(long fieldId, Func<ModelField, ModelField> updater)
{ {
ModelField field; ModelField field;

25
src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs

@ -30,15 +30,15 @@ namespace PinkParrot.Core.Schema
get { return minValue; } get { return minValue; }
} }
public NumberField(Guid id, string name) public NumberField(long id, string name)
: base(id, name) : base(id, name)
{ {
} }
protected override void ConfigureCore(PropertiesBag settings, ICollection<string> errors) protected override void ConfigureCore(dynamic settings, ICollection<string> errors)
{ {
maxValue = ParseNumber("MaxValue", settings, errors); maxValue = settings.MaxValue;
minValue = ParseNumber("MinValue", settings, errors); minValue = settings.MinValue;
if (maxValue.HasValue && minValue.HasValue && minValue.Value > maxValue.Value) 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<string> 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<string> errors) protected override Task ValidateCoreAsync(PropertyValue property, ICollection<string> errors)
{ {
try try

7
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs

@ -6,17 +6,16 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.CQRS;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldAdded : IEvent public class ModelFieldAdded : IEvent
{ {
public Guid FieldId { get; set; } public long FieldId;
public string FieldType; public string FieldType;
public string FieldName { get; set; } public string FieldName;
} }
} }

5
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs

@ -6,13 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.CQRS;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldDeleted : IEvent public class ModelFieldDeleted : IEvent
{ {
public Guid FieldId; public long FieldId;
} }
} }

5
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs

@ -6,13 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.CQRS;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldDisabled : IEvent public class ModelFieldDisabled : IEvent
{ {
public Guid FieldId; public long FieldId;
} }
} }

5
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs

@ -6,13 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.CQRS;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldEnabled : IEvent public class ModelFieldEnabled : IEvent
{ {
public Guid FieldId; public long FieldId;
} }
} }

5
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs

@ -6,13 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.CQRS;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldHidden : IEvent public class ModelFieldHidden : IEvent
{ {
public Guid FieldId; public long FieldId;
} }
} }

5
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs

@ -6,13 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.CQRS;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldShown : IEvent public class ModelFieldShown : IEvent
{ {
public Guid FieldId; public long FieldId;
} }
} }

5
src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs

@ -6,15 +6,14 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {
public class ModelFieldUpdated : IEvent public class ModelFieldUpdated : IEvent
{ {
public Guid FieldId { get; set; } public long FieldId;
public PropertiesBag Settings { get; set; } public PropertiesBag Settings { get; set; }
} }

2
src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs

@ -6,7 +6,7 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {

2
src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs

@ -6,7 +6,7 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {

2
src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs

@ -7,7 +7,7 @@
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Events.Schema namespace PinkParrot.Events.Schema
{ {

0
src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTest.cs → src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs

284
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<int> DispatchEventAsync(IEvent @event)
{
return this.DispatchFuncAsync(@event, 9);
}
public Task<int> DispatchEventAsync(IEvent @event, int context)
{
return this.DispatchFuncAsync(@event, context, 13);
}
public Task<int> On(EventA @event)
{
return Task.FromResult(++EventATriggered);
}
public Task<int> On(EventB @event)
{
return Task.FromResult(++EventBTriggered);
}
public Task<int> On(EventA @event, int context)
{
return Task.FromResult(++EventATriggered + context);
}
public Task<int> 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<bool> DispatchEventAsync(IEvent @event)
{
return this.DispatchActionAsync(@event);
}
public Task<bool> 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);
}
}
}

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTest.cs → src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs

@ -11,7 +11,7 @@ using Xunit;
namespace PinkParrot.Infrastructure namespace PinkParrot.Infrastructure
{ {
public sealed class EnumExtensionsTest public sealed class EnumExtensionsTests
{ {
[Fact] [Fact]
public void Should_return_true_if_enum_is_valid() public void Should_return_true_if_enum_is_valid()

40
src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs

@ -9,7 +9,9 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using NodaTime; using NodaTime;
using PinkParrot.Infrastructure.Json;
using Xunit; using Xunit;
// ReSharper disable PossibleInvalidOperationException // ReSharper disable PossibleInvalidOperationException
@ -28,6 +30,32 @@ namespace PinkParrot.Infrastructure
dynamicBag = bag; 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<PropertiesBag>(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] [Fact]
public void Should_return_false_when_renaming_unknown_property() public void Should_return_false_when_renaming_unknown_property()
{ {
@ -119,6 +147,18 @@ namespace PinkParrot.Infrastructure
Assert.Throws<InvalidOperationException>(() => bag.Set("Key", (byte)1)); Assert.Throws<InvalidOperationException>(() => 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] [Fact]
public void Should_convert_string_to_numbers() public void Should_convert_string_to_numbers()
{ {

17
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; }
}
}

72
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;
}
}
}

20
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<T>(this IDomainObjectFactory factory, Guid id) where T : DomainObject
{
return (T)factory.CreateNew(typeof(T), id);
}
}
}

14
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
{
}
}

17
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);
}
}

17
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<bool> HandleAsync(CommandContext context);
}
}

17
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);
}
}

20
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<TDomainObject> GetByIdAsync<TDomainObject>(Guid id, int version = 0) where TDomainObject : class, IAggregate;
Task SaveAsync(IAggregate domainObject, Guid commitId);
}
}

59
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<ICommandHandler> handlers;
public InMemoryCommandBus(
IDomainObjectRepository repository,
IDomainObjectFactory factory,
IEnumerable<ICommandHandler> 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);
}
}
}
}
}

16
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Infrastructure.CQRS namespace PinkParrot.Infrastructure.CQRS
{ {
@ -37,6 +38,8 @@ namespace PinkParrot.Infrastructure.CQRS
this.version = version; this.version = version;
} }
protected abstract void ApplyEvent(IEvent @event);
protected void RaiseEvent<TEvent>(Envelope<TEvent> envelope, bool disableApply = false) where TEvent : class, IEvent protected void RaiseEvent<TEvent>(Envelope<TEvent> envelope, bool disableApply = false) where TEvent : class, IEvent
{ {
Guard.NotNull(envelope, nameof(envelope)); 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) void IAggregate.ApplyEvent(IEvent @event)
{ {
Apply(@event as dynamic); ApplyEvent(@event); version++;
version++;
} }
void IAggregate.ClearUncommittedEvents() void IAggregate.ClearUncommittedEvents()

1
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeFactory.cs

@ -8,6 +8,7 @@
using System; using System;
using NodaTime; using NodaTime;
using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Infrastructure.CQRS namespace PinkParrot.Infrastructure.CQRS
{ {

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IEvent.cs → src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Events/IEvent.cs

@ -5,7 +5,7 @@
// Copyright (c) PinkParrot Group // Copyright (c) PinkParrot Group
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
namespace PinkParrot.Infrastructure.CQRS namespace PinkParrot.Infrastructure.CQRS.Events
{ {
public interface IEvent public interface IEvent
{ {

15
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<IEvent> @event);
}
}

1
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using PinkParrot.Infrastructure.CQRS.Events;
namespace PinkParrot.Infrastructure.CQRS namespace PinkParrot.Infrastructure.CQRS
{ {

45
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<TTarget, TIn, TContext>
{
private static readonly Dictionary<Type, Action<TTarget, object, TContext>> Handlers;
static ActionContextDispatcher()
{
Handlers =
typeof(TTarget)
.GetMethods()
.Where(Helper.HasRightName)
.Where(Helper.HasRightParameters<TIn, TContext>)
.Select(ActionContextDispatcherFactory.CreateActionHandler<TTarget, TContext>)
.ToDictionary(h => h.Item1, h => h.Item2);
}
public static bool Dispatch(TTarget target, TIn input, TContext context)
{
Action<TTarget, object, TContext> handler;
if (!Handlers.TryGetValue(input.GetType(), out handler))
{
return false;
}
handler(target, input, context);
return true;
}
}
}

39
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<Type, Action<TTarget, object, TContext>> CreateActionHandler<TTarget, TContext>(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<Type, Action<TTarget, object, TContext>>(inputType, (Action<TTarget, object, TContext>)handler);
}
private static Action<TTarget, object, TContext> Factory<TTarget, TIn, TContext>(MethodInfo methodInfo)
{
var type = typeof(Action<TTarget, TIn, TContext>);
var handler = (Action<TTarget, TIn, TContext>)methodInfo.CreateDelegate(type);
return (target, input, context) => handler(target, (TIn)input, context);
}
}
}

45
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<TTarget, TIn>
{
private static readonly Dictionary<Type, Action<TTarget, object>> Handlers;
static ActionDispatcher()
{
Handlers =
typeof(TTarget)
.GetMethods()
.Where(Helper.HasRightName)
.Where(Helper.HasRightParameters<TIn>)
.Select(ActionDispatcherFactory.CreateActionHandler<TTarget>)
.ToDictionary(h => h.Item1, h => h.Item2);
}
public static bool Dispatch(TTarget target, TIn item)
{
Action<TTarget, object> handler;
if (!Handlers.TryGetValue(item.GetType(), out handler))
{
return false;
}
handler(target, item);
return true;
}
}
}

39
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<Type, Action<T, object>> CreateActionHandler<T>(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<Type, Action<T, object>>(inputType, (Action<T, object>)handler);
}
private static Action<TTarget, object> Factory<TTarget, TIn>(MethodInfo methodInfo)
{
var type = typeof(Action<TTarget, TIn>);
var handler = (Action<TTarget, TIn>)methodInfo.CreateDelegate(type);
return (target, input) => handler(target, (TIn)input);
}
}
}

81
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<TTarget, TIn>(this TTarget target, TIn input)
{
return ActionDispatcher<TTarget, TIn>.Dispatch(target, input);
}
public static bool DispatchAction<TTarget, TIn, TContext>(this TTarget target, TIn input, TContext context)
{
return ActionContextDispatcher<TTarget, TIn, TContext>.Dispatch(target, input, context);
}
public static async Task<bool> DispatchActionAsync<TTarget, TIn>(this TTarget target, TIn input)
{
var task = FuncDispatcher<TTarget, TIn, Task>.Dispatch(target, input);
if (task == null)
{
return false;
}
await task;
return true;
}
public static async Task<bool> DispatchActionAsync<TTarget, TIn, TContext>(this TTarget target, TIn input, TContext context)
{
var task = FuncContextDispatcher<TTarget, TIn, TContext, Task>.Dispatch(target, input, context);
if (task == null)
{
return false;
}
await task;
return true;
}
public static TOut DispatchFunc<TTarget, TIn, TOut>(this TTarget target, TIn input, TOut fallback)
{
var result = FuncDispatcher<TTarget, TIn, TOut>.Dispatch(target, input);
return Equals(result, default(TOut)) ? fallback : result;
}
public static TOut DispatchFunc<TTarget, TIn, TContext, TOut>(this TTarget target, TIn input, TContext context, TOut fallback)
{
var result = FuncContextDispatcher<TTarget, TIn, TContext, TOut>.Dispatch(target, input, context);
return Equals(result, default(TOut)) ? fallback : result;
}
public static Task<TOut> DispatchFuncAsync<TTarget, TIn, TOut>(this TTarget target, TIn input, TOut fallback)
{
var result = FuncDispatcher<TTarget, TIn, Task<TOut>>.Dispatch(target, input);
return result ?? Task.FromResult(fallback);
}
public static Task<TOut> DispatchFuncAsync<TTarget, TIn, TContext, TOut>(this TTarget target, TIn input, TContext context, TOut fallback)
{
var result = FuncContextDispatcher<TTarget, TIn, TContext, Task<TOut>>.Dispatch(target, input, context);
return result ?? Task.FromResult(fallback);
}
}
}

44
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<TTarget, TIn, TContext, TOut>
{
private static readonly Dictionary<Type, Func<TTarget, object, TContext, TOut>> Handlers;
static FuncContextDispatcher()
{
Handlers =
typeof(TTarget)
.GetMethods()
.Where(Helper.HasRightName)
.Where(Helper.HasRightParameters<TIn, TContext>)
.Where(Helper.HasRightReturnType<TOut>)
.Select(FuncContextDispatcherFactory.CreateFuncHandler<TTarget, TContext, TOut >)
.ToDictionary(h => h.Item1, h => h.Item2);
}
public static TOut Dispatch(TTarget target, TIn item, TContext context)
{
Func<TTarget, object, TContext, TOut > handler;
if (Handlers.TryGetValue(item.GetType(), out handler))
{
return handler(target, item, context);
}
return default(TOut);
}
}
}

39
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<Type, Func<TTarget, object, TContext, TOut>> CreateFuncHandler<TTarget, TContext, TOut>(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<Type, Func<TTarget, object, TContext, TOut>>(inputType, (Func<TTarget, object, TContext, TOut>)handler);
}
private static Func<TTarget, object, TContext, TOut> Factory<TTarget, TIn, TContext, TOut>(MethodInfo methodInfo)
{
var type = typeof(Func<TTarget, TIn, TContext, TOut>);
var handler = (Func<TTarget, TIn, TContext, TOut>)methodInfo.CreateDelegate(type);
return (target, input, context) => handler(target, (TIn)input, context);
}
}
}

44
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<TTarget, TIn, TOut>
{
private static readonly Dictionary<Type, Func<TTarget, object, TOut>> Handlers;
static FuncDispatcher()
{
Handlers =
typeof(TTarget)
.GetMethods()
.Where(Helper.HasRightName)
.Where(Helper.HasRightParameters<TIn>)
.Where(Helper.HasRightReturnType<TOut>)
.Select(FuncDispatcherFactory.CreateFuncHandler<TTarget, TOut>)
.ToDictionary(h => h.Item1, h => h.Item2);
}
public static TOut Dispatch(TTarget target, TIn item)
{
Func<TTarget, object, TOut> handler;
if (Handlers.TryGetValue(item.GetType(), out handler))
{
return handler(target, item);
}
return default(TOut);
}
}
}

39
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<Type, Func<TTarget, object, TReturn>> CreateFuncHandler<TTarget, TReturn>(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<Type, Func<TTarget, object, TReturn>>(inputType, (Func<TTarget, object, TReturn>)handler);
}
private static Func<TTarget, object, TReturn> Factory<TTarget, TIn, TReturn>(MethodInfo methodInfo)
{
var type = typeof(Func<TTarget, TIn, TReturn>);
var handler = (Func<TTarget, TIn, TReturn>)methodInfo.CreateDelegate(type);
return (target, input) => handler(target, (TIn)input);
}
}
}

39
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<TOut>(MethodInfo method)
{
return method.ReturnType == typeof(TOut);
}
public static bool HasRightParameters<TIn>(MethodInfo method)
{
var parameters = method.GetParameters();
return parameters.Length == 1 && typeof(TIn).IsAssignableFrom(parameters[0].ParameterType);
}
public static bool HasRightParameters<TIn, TContext>(MethodInfo method)
{
var parameters = method.GetParameters();
return parameters.Length == 2 && typeof(TIn).IsAssignableFrom(parameters[0].ParameterType) && parameters[1].ParameterType == typeof(TContext);
}
}
}

15
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; }
}
}

76
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();
}
}
}

7
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertiesBag.cs

@ -14,6 +14,7 @@ namespace PinkParrot.Infrastructure
{ {
public class PropertiesBag : DynamicObject public class PropertiesBag : DynamicObject
{ {
private static readonly PropertyValue FallbackValue = new PropertyValue(null);
private readonly Dictionary<string, PropertyValue> internalDictionary = new Dictionary<string, PropertyValue>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, PropertyValue> internalDictionary = new Dictionary<string, PropertyValue>(StringComparer.OrdinalIgnoreCase);
public int Count public int Count
@ -37,7 +38,7 @@ namespace PinkParrot.Infrastructure
{ {
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); 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) 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) public override bool TrySetMember(SetMemberBinder binder, object value)

7
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json

@ -1,8 +1,11 @@
{ {
"version": "1.0.0-*", "version": "1.0.0-*",
"dependencies": { "dependencies": {
"NodaTime": "2.0.0-alpha20160729", "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": { "frameworks": {
"netcoreapp1.0": { "netcoreapp1.0": {

6
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class AddModelField public class AddModelField : AggregateCommand
{ {
public Guid AggregateId;
public string FieldType; public string FieldType;
public string FieldName; public string FieldName;

6
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class CreateModelSchema public class CreateModelSchema : AggregateCommand
{ {
public Guid AggregateId;
public string Name; public string Name;
} }
} }

8
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class DeleteModelField public class DeleteModelField : AggregateCommand
{ {
public Guid AggregateId; public long FieldId;
public Guid FieldId;
} }
} }

5
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs

@ -6,12 +6,11 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class DeleteModelSchema public class DeleteModelSchema : AggregateCommand
{ {
public Guid AggregateId;
} }
} }

8
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class DisableModelField public class DisableModelField : AggregateCommand
{ {
public Guid AggregateId; public long FieldId;
public Guid FieldId;
} }
} }

8
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class EnableModelField public class EnableModelField : AggregateCommand
{ {
public Guid AggregateId; public long FieldId;
public Guid FieldId;
} }
} }

8
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class HideModelField public class HideModelField : AggregateCommand
{ {
public Guid AggregateId; public long FieldId;
public Guid FieldId;
} }
} }

8
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs

@ -6,14 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System; using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class ShowModelField public class ShowModelField : AggregateCommand
{ {
public Guid AggregateId; public long FieldId;
public Guid FieldId;
} }
} }

8
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs

@ -6,16 +6,14 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class UpdateModelField public class UpdateModelField : AggregateCommand
{ {
public Guid AggregateId; public long FieldId;
public Guid FieldId;
public PropertiesBag Settings; public PropertiesBag Settings;
} }

6
src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs

@ -6,15 +6,13 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class UpdateModelSchema public class UpdateModelSchema : AggregateCommand
{ {
public Guid AggregateId;
public string NewName; public string NewName;
public PropertiesBag Settings { get; set; } public PropertiesBag Settings { get; set; }

87
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<bool> 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<ModelSchemaDomainObject>(command.AggregateId);
schema.Create(command);
return context.Repository.SaveAsync(schema, Guid.NewGuid());
}
private async Task Update(AggregateCommand command, CommandContext context, Action<ModelSchemaDomainObject> updater)
{
var schema = await context.Repository.GetByIdAsync<ModelSchemaDomainObject>(command.AggregateId);
updater(schema);
await context.Repository.SaveAsync(schema, Guid.NewGuid());
}
}
}

61
src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs

@ -10,6 +10,8 @@ using System;
using PinkParrot.Core.Schema; using PinkParrot.Core.Schema;
using PinkParrot.Events.Schema; using PinkParrot.Events.Schema;
using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS;
using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Write.Schema.Commands; using PinkParrot.Write.Schema.Commands;
namespace PinkParrot.Write.Schema namespace PinkParrot.Write.Schema
@ -18,14 +20,9 @@ namespace PinkParrot.Write.Schema
{ {
private readonly ModelFieldFactory fieldFactory; private readonly ModelFieldFactory fieldFactory;
private bool isDeleted; private bool isDeleted;
private long totalFields;
private ModelSchema schema; private ModelSchema schema;
public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory fieldFactory)
: base(id, version)
{
this.fieldFactory = fieldFactory;
}
public ModelSchema Schema public ModelSchema Schema
{ {
get { return schema; } get { return schema; }
@ -36,14 +33,22 @@ namespace PinkParrot.Write.Schema
get { return isDeleted; } 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) protected void Apply(ModelFieldAdded @event)
{ {
schema = schema.AddField(@event.FieldId, @event.FieldType, @event.FieldName, fieldFactory); 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) protected void Apply(ModelFieldUpdated @event)
@ -86,23 +91,24 @@ namespace PinkParrot.Write.Schema
isDeleted = false; 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( RaiseEvent(new ModelSchemaCreated { Name = command.Name }, true);
new ModelFieldAdded {FieldId = id, FieldType = command.FieldType, FieldName = command.FieldName}, true);
} }
public void Update(UpdateModelSchema command) public void Update(UpdateModelSchema command)
@ -111,7 +117,7 @@ namespace PinkParrot.Write.Schema
schema = schema.Update(schema.Metadata.Configure(command.NewName, command.Settings)); 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) public void UpdateField(UpdateModelField command)
@ -120,7 +126,7 @@ namespace PinkParrot.Write.Schema
schema = schema.SetField(command.FieldId, command.Settings); 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) public void HideField(HideModelField command)
@ -129,16 +135,16 @@ namespace PinkParrot.Write.Schema
schema = schema.HideField(command.FieldId); 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(); VerifyCreatedAndNotDeleted();
schema = schema.ShowField(command.FieldId); schema = schema.ShowField(command.FieldId);
RaiseEvent(new ModelFieldShown {FieldId = command.FieldId}, true); RaiseEvent(new ModelFieldShown { FieldId = command.FieldId }, true);
} }
public void DisableField(DisableModelField command) public void DisableField(DisableModelField command)
@ -147,7 +153,7 @@ namespace PinkParrot.Write.Schema
schema = schema.DisableField(command.FieldId); schema = schema.DisableField(command.FieldId);
RaiseEvent(new ModelFieldDisabled {FieldId = command.FieldId}, true); RaiseEvent(new ModelFieldDisabled { FieldId = command.FieldId }, true);
} }
public void EnableField(EnableModelField command) public void EnableField(EnableModelField command)
@ -156,7 +162,7 @@ namespace PinkParrot.Write.Schema
schema = schema.EnableField(command.FieldId); schema = schema.EnableField(command.FieldId);
RaiseEvent(new ModelFieldEnabled {FieldId = command.FieldId}, true); RaiseEvent(new ModelFieldEnabled { FieldId = command.FieldId }, true);
} }
public void Delete(DeleteModelSchema command) public void Delete(DeleteModelSchema command)
@ -174,7 +180,7 @@ namespace PinkParrot.Write.Schema
schema = schema.DeleteField(command.FieldId); schema = schema.DeleteField(command.FieldId);
RaiseEvent(new ModelFieldDeleted {FieldId = command.FieldId}, true); RaiseEvent(new ModelFieldDeleted { FieldId = command.FieldId }, true);
} }
private void VerifyNotCreated() private void VerifyNotCreated()
@ -192,5 +198,10 @@ namespace PinkParrot.Write.Schema
throw new InvalidOperationException("Schema has already been deleted or not created yet."); throw new InvalidOperationException("Schema has already been deleted or not created yet.");
} }
} }
protected override void ApplyEvent(IEvent @event)
{
this.DispatchAction(@event);
}
} }
} }
Loading…
Cancel
Save