mirror of https://github.com/Squidex/squidex.git
62 changed files with 1402 additions and 364 deletions
@ -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() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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> |
|
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ICommand.cs
|
||||
|
// PinkParrot Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) PinkParrot Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace PinkParrot.Infrastructure.CQRS.Commands |
||||
|
{ |
||||
|
public interface ICommand |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue