Browse Source

Same very first progress

pull/1/head
Sebastian Stehle 9 years ago
parent
commit
0ec44f6a65
  1. 58
      src/PinkParrot/Configurations/InfrastructureDependencies.cs
  2. 24
      src/PinkParrot/Configurations/ReadDependencies.cs
  3. 57
      src/PinkParrot/Configurations/Serializers.cs
  4. 44
      src/PinkParrot/Configurations/Swagger.cs
  5. 27
      src/PinkParrot/Configurations/WriteDependencies.cs
  6. 19
      src/PinkParrot/Modules/Api/EntityCreated.cs
  7. 64
      src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs
  8. 59
      src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
  9. 29
      src/PinkParrot/Pipeline/Swagger/CamelCaseParameterFilter.cs
  10. 65
      src/PinkParrot/Pipeline/Swagger/HidePropertyFilter.cs
  11. 36
      src/PinkParrot/Pipeline/Swagger/RemoveReadonlyFilter.cs
  12. 42
      src/PinkParrot/Startup.cs
  13. 9
      src/PinkParrot/project.json
  14. 4
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs
  15. 10
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
  16. 2
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaProperties.cs
  17. 2
      src/pinkparrot_core/PinkParrot.Core/Schema/NamedElementProperties.cs
  18. 10
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Autofac/AutofacDomainObjectFactory.cs
  19. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/AggregateCommand.cs
  20. 12
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IAggregateCommand.cs
  21. 18
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Envelope.cs
  22. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeHeaders.cs
  23. 33
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/DefaultNameResolver.cs
  24. 153
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreDomainObjectRepository.cs
  25. 66
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreFormatter.cs
  26. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/IStreamNameResolver.cs
  27. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs
  28. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs
  29. 9
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs
  30. 9
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs
  31. 25
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectDeletedException.cs
  32. 50
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectException.cs
  33. 25
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectNotFoundException.cs
  34. 47
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectVersionException.cs
  35. 17
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/HideAttribute.cs
  36. 3
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/PropertiesBagConverter.cs
  37. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertyValue.cs
  38. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json
  39. 5
      src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaRM.cs
  40. 18
      src/pinkparrot_read/PinkParrot.Read/Services/ISchemaProvider.cs
  41. 21
      src/pinkparrot_read/PinkParrot.Read/Services/Implementations/SchemaProvider.cs
  42. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
  43. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs
  44. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
  45. 4
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs
  46. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs
  47. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
  48. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
  49. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
  50. 4
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
  51. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs
  52. 24
      src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs

58
src/PinkParrot/Configurations/InfrastructureDependencies.cs

@ -0,0 +1,58 @@
// ==========================================================================
// InfrastructureDependencies.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.Net;
using Autofac;
using EventStore.ClientAPI;
using EventStore.ClientAPI.SystemData;
using PinkParrot.Infrastructure.CQRS.Autofac;
using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Infrastructure.CQRS.EventStore;
namespace PinkParrot.Configurations
{
public class InfrastructureDependencies : Module
{
protected override void Load(ContainerBuilder builder)
{
var eventStore =
EventStoreConnection.Create(
ConnectionSettings.Create()
.UseConsoleLogger()
.UseDebugLogger()
.KeepReconnecting()
.KeepRetrying(),
new IPEndPoint(IPAddress.Loopback, 1113));
eventStore.ConnectAsync().Wait();
builder.RegisterInstance(new UserCredentials("admin", "changeit"))
.SingleInstance();
builder.RegisterType<AutofacDomainObjectFactory>()
.As<IDomainObjectFactory>()
.SingleInstance();
builder.RegisterType<DefaultNameResolver>()
.As<IStreamNameResolver>()
.SingleInstance();
builder.RegisterInstance(eventStore)
.As<IEventStoreConnection>()
.SingleInstance();
builder.RegisterType<EventStoreDomainObjectRepository>()
.As<IDomainObjectRepository>()
.SingleInstance();
builder.RegisterType<InMemoryCommandBus>()
.As<ICommandBus>()
.SingleInstance();
}
}
}

24
src/PinkParrot/Configurations/ReadDependencies.cs

@ -0,0 +1,24 @@
// ==========================================================================
// ReadDependencies.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using Autofac;
using PinkParrot.Read.Services;
using PinkParrot.Read.Services.Implementations;
namespace PinkParrot.Configurations
{
public sealed class ReadDependencies : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<SchemaProvider>()
.As<ISchemaProvider>()
.SingleInstance();
}
}
}

57
src/PinkParrot/Configurations/Serializers.cs

@ -0,0 +1,57 @@
// ==========================================================================
// Serializers.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.EventStore;
using PinkParrot.Infrastructure.Json;
namespace PinkParrot.Configurations
{
public static class Serializers
{
private static JsonSerializerSettings ConfigureJson(JsonSerializerSettings settings)
{
settings.Binder = new TypeNameSerializationBinder().Map(typeof(ModelSchema).GetTypeInfo().Assembly);
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.Converters.Add(new PropertiesBagConverter());
settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
settings.DateParseHandling = DateParseHandling.DateTime;
settings.TypeNameHandling = TypeNameHandling.Auto;
return settings;
}
private static JsonSerializerSettings CreateSettings()
{
return ConfigureJson(new JsonSerializerSettings());
}
public static void AddEventFormatter(this IServiceCollection services)
{
var fieldFactory =
new ModelFieldFactory()
.AddFactory<NumberFieldProperties>(id => new NumberField(id));
services.AddSingleton(t => CreateSettings());
services.AddSingleton(fieldFactory);
services.AddSingleton<EventStoreParser>();
}
public static void AddAppSerializers(this IMvcBuilder mvc)
{
mvc.AddJsonOptions(options =>
{
ConfigureJson(options.SerializerSettings);
});
}
}
}

44
src/PinkParrot/Configurations/Swagger.cs

@ -0,0 +1,44 @@
// ==========================================================================
// Swagger.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
using PinkParrot.Pipeline.Swagger;
using Swashbuckle.Swagger.Model;
namespace PinkParrot.Configurations
{
public static class Swagger
{
public static void AddAppSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(options =>
{
options.SingleApiVersion(new Info { Title = "Pink Parrot", Version = "v1" });
options.OperationFilter<HidePropertyFilter>();
options.OperationFilter<CamelCaseParameterFilter>();
options.SchemaFilter<HidePropertyFilter>();
options.SchemaFilter<RemoveReadonlyFilter>();
options.IncludeXmlComments(GetXmlCommentsPath(PlatformServices.Default.Application));
});
}
public static void UseAppSwagger(this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUi();
}
private static string GetXmlCommentsPath(ApplicationEnvironment appEnvironment)
{
return Path.Combine(appEnvironment.ApplicationBasePath, "PinkParrot.xml");
}
}
}

27
src/PinkParrot/Configurations/WriteDependencies.cs

@ -0,0 +1,27 @@
// ==========================================================================
// WriteDependencies.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using Autofac;
using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Write.Schema;
namespace PinkParrot.Configurations
{
public class WriteDependencies : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ModelSchemaCommandHandler>()
.As<ICommandHandler>()
.SingleInstance();
builder.RegisterType<ModelSchemaDomainObject>()
.InstancePerDependency();
}
}
}

19
src/PinkParrot/Modules/Api/EntityCreated.cs

@ -0,0 +1,19 @@
// ==========================================================================
// EntityCreated.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
namespace PinkParrot.Modules.Api
{
public class EntityCreated
{
[Required]
public Guid Id { get; set; }
}
}

64
src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs

@ -8,8 +8,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.Commands; using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Write.Schema.Commands; using PinkParrot.Write.Schema.Commands;
using Swashbuckle.SwaggerGen.Annotations;
#pragma warning disable 1584,1711,1572,1573,1581,1580
namespace PinkParrot.Modules.Api.Schemas namespace PinkParrot.Modules.Api.Schemas
{ {
@ -22,51 +26,97 @@ namespace PinkParrot.Modules.Api.Schemas
this.commandBus = commandBus; this.commandBus = commandBus;
} }
/// <summary>
/// Adds a new field to the schema with the specified name.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="command">The field properties</param>
[HttpPost] [HttpPost]
[Route("schemas/{name}/fields/")] [Route("schemas/{name}/fields/")]
public Task Add(AddModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Add(string name, [FromBody] ModelFieldProperties field)
{ {
var command = new AddModelField { Properties = field };
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }
/// <summary>
/// Uüdates the field with the specified schema name and field id.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="fieldId">The id of the field.</param>
/// <param name="command">The field properties</param>
[HttpPut] [HttpPut]
[Route("schemas/{name}/fields/{fieldId:long}/")] [Route("schemas/{name}/fields/{fieldId:long}/")]
public Task Update(UpdateModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Update(string name, long fieldId, [FromBody] UpdateModelField command)
{ {
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }
/// <summary>
/// Hides the field with the specified schema name and field id.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="fieldId">The id of the field.</param>
[HttpPut] [HttpPut]
[Route("schemas/{name}/fields/{fieldId:long}/hide/")] [Route("schemas/{name}/fields/{fieldId:long}/hide/")]
public Task Hide(HideModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Hide(string name, long fieldId, HideModelField command)
{ {
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }
/// <summary>
/// Sows the field with the specified schema name and field id.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="fieldId">The id of the field.</param>
[HttpPut] [HttpPut]
[Route("schemas/{name}/fields/{fieldId:long}/show/")] [Route("schemas/{name}/fields/{fieldId:long}/show/")]
public Task Show(ShowModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Show(string name, long fieldId, ShowModelField command)
{ {
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }
/// <summary>
/// Enables the field with the specified schema name and field id.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="fieldId">The id of the field.</param>
[HttpPut] [HttpPut]
[Route("schemas/{name}/fields/{fieldId:long}/enable/")] [Route("schemas/{name}/fields/{fieldId:long}/enable/")]
public Task Enable(EnableModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Enable(string name, long fieldId, EnableModelField command)
{ {
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }
/// <summary>
/// Disables the field with the specified schema name and field id.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="fieldId">The id of the field.</param>
[HttpPut] [HttpPut]
[Route("schemas/{name}/fields/{fieldId:long}/disable/")] [Route("schemas/{name}/fields/{fieldId:long}/disable/")]
public Task Enable(DisableModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Disable(string name, long fieldId, DisableModelField command)
{ {
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }
/// <summary>
/// Deletes the field with the specified schema name and field id.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="fieldId">The id of the field.</param>
[HttpDelete] [HttpDelete]
[Route("schemas/{name}/fields/{fieldId:long}/")] [Route("schemas/{name}/fields/{fieldId:long}/")]
public Task Delete(DeleteModelField command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Delete(string name, long fieldId, DeleteModelField command)
{ {
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
} }

59
src/PinkParrot/Modules/Api/Schemas/SchemasController.cs

@ -6,12 +6,17 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.Commands; using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Read.Models; using PinkParrot.Read.Models;
using PinkParrot.Write.Schema.Commands; using PinkParrot.Write.Schema.Commands;
using Swashbuckle.SwaggerGen.Annotations;
#pragma warning disable 1584,1711,1572,1581,1580
namespace PinkParrot.Modules.Api.Schemas namespace PinkParrot.Modules.Api.Schemas
{ {
@ -24,32 +29,74 @@ namespace PinkParrot.Modules.Api.Schemas
this.commandBus = commandBus; this.commandBus = commandBus;
} }
/// <summary>
/// Queries all your schemas.
/// </summary>
[HttpGet] [HttpGet]
[Route("schemas/")] [Route("schemas/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task<List<ModelSchemaRM>> Query() public Task<List<ModelSchemaRM>> Query()
{ {
return null; return null;
} }
/// <summary>
/// Creates a new schema.
/// </summary>
/// <param name="schema">The properties of the schema.</param>
/// <remarks>
/// Field can be managed later.
/// </remarks>
/// <response code="201">Schema created</response>
/// <response code="500">Schema update failed</response>
[HttpPost] [HttpPost]
[Route("schemas/")] [Route("schemas/")]
public Task Create(CreateModelSchema command) [SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(EntityCreated), 204)]
public async Task<ActionResult> Create([FromBody] ModelSchemaProperties schema)
{ {
return commandBus.PublishAsync(command); var command = new CreateModelSchema { AggregateId = Guid.NewGuid(), Properties = schema };
await commandBus.PublishAsync(command);
return CreatedAtAction("Query", new EntityCreated { Id = command.AggregateId });
} }
/// <summary>
/// Updates the schema with the specified name.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <param name="schema">The properties of the schema.</param>
/// <response code="204">Schema update</response>
/// <response code="404">Schema not found</response>
/// <response code="500">Schema update failed</response>
[HttpPut] [HttpPut]
[Route("schemas/{name}/")] [Route("schemas/{name}/")]
public Task Update(UpdateModelSchema command) [SwaggerOperation(Tags = new[] { "Schemas" })]
public async Task<ActionResult> Update(string name, [FromBody] ModelSchemaProperties schema)
{ {
return commandBus.PublishAsync(command); var command = new UpdateModelSchema { Properties = schema };
await commandBus.PublishAsync(command);
return NoContent();
} }
/// <summary>
/// Deletes the schema with the specified name.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <response code="204">Schema deleted</response>
/// <response code="404">Schema not found</response>
/// <response code="500">Schema deletion failed</response>
[HttpDelete] [HttpDelete]
[Route("schemas/{name}/")] [Route("schemas/{name}/")]
public Task Delete() [SwaggerOperation(Tags = new[] { "Schemas" })]
public async Task<ActionResult> Delete(string name)
{ {
return commandBus.PublishAsync(new DeleteModelSchema()); await commandBus.PublishAsync(new DeleteModelSchema());
return NoContent();
} }
} }
} }

29
src/PinkParrot/Pipeline/Swagger/CamelCaseParameterFilter.cs

@ -0,0 +1,29 @@
// ==========================================================================
// CamelCaseParameterFilter.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using Swashbuckle.Swagger.Model;
using Swashbuckle.SwaggerGen.Generator;
namespace PinkParrot.Pipeline.Swagger
{
public sealed class CamelCaseParameterFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
{
return;
}
foreach (var parameter in operation.Parameters)
{
parameter.Name = char.ToLowerInvariant(parameter.Name[0]) + parameter.Name.Substring(1);
}
}
}
}

65
src/PinkParrot/Pipeline/Swagger/HidePropertyFilter.cs

@ -0,0 +1,65 @@
// ==========================================================================
// HidePropertyFilter.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using PinkParrot.Infrastructure;
using Swashbuckle.Swagger.Model;
using Swashbuckle.SwaggerGen.Generator;
namespace PinkParrot.Pipeline.Swagger
{
public class HidePropertyFilter : ISchemaFilter, IOperationFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
foreach (var property in context.JsonContract.UnderlyingType.GetProperties())
{
var attribute = property.GetCustomAttribute<HideAttribute>();
if (attribute != null)
{
model.Properties.Remove(property.Name);
}
}
}
public void Apply(Operation operation, OperationFilterContext context)
{
if (context?.ApiDescription.ParameterDescriptions == null)
{
return;
}
if (operation.Parameters == null)
{
return;
}
foreach (var parameterDescription in context.ApiDescription.ParameterDescriptions)
{
var metadata = parameterDescription.ModelMetadata as DefaultModelMetadata;
var hasAttribute = metadata?.Attributes?.Attributes.OfType<HideAttribute>().Any();
if (hasAttribute != true)
{
continue;
}
var parameter = operation.Parameters.FirstOrDefault(p => p.Name == parameterDescription.Name);
if (parameter != null)
{
operation.Parameters.Remove(parameter);
}
}
}
}
}

36
src/PinkParrot/Pipeline/Swagger/RemoveReadonlyFilter.cs

@ -0,0 +1,36 @@
// ==========================================================================
// RemoveReadonlyFilter.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using Swashbuckle.Swagger.Model;
using Swashbuckle.SwaggerGen.Generator;
namespace PinkParrot.Pipeline.Swagger
{
public class RemoveReadonlyFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
Apply(model);
}
private static void Apply(Schema model)
{
model.ReadOnly = null;
if (model.Properties == null)
{
return;
}
foreach (var property in model.Properties)
{
Apply(property.Value);
}
}
}
}

42
src/PinkParrot/Startup.cs

@ -11,11 +11,9 @@ using Autofac;
using Autofac.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PinkParrot.Infrastructure.CQRS.Autofac; using PinkParrot.Configurations;
using PinkParrot.Infrastructure.CQRS.Commands;
// ReSharper disable AccessToModifiedClosure // ReSharper disable AccessToModifiedClosure
@ -25,30 +23,18 @@ namespace PinkParrot
{ {
public IServiceProvider ConfigureServices(IServiceCollection services) public IServiceProvider ConfigureServices(IServiceCollection services)
{ {
services.AddMvc(); services.AddMvc().AddAppSerializers();
services.AddRouting(); services.AddRouting();
services.AddSwaggerGen(); services.AddAppSwagger();
services.AddEventFormatter();
IContainer container = null; var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule<WriteDependencies>();
builder.RegisterModule<InfrastructureDependencies>();
builder.RegisterModule<ReadDependencies>();
var containerBuilder = new ContainerBuilder(); return new AutofacServiceProvider(builder.Build());
containerBuilder.Populate(services);
containerBuilder.Register(c => container)
.As<IContainer>()
.SingleInstance();
containerBuilder.RegisterType<AutofacDomainObjectFactory>()
.As<IDomainObjectFactory>()
.SingleInstance();
containerBuilder.RegisterType<InMemoryCommandBus>()
.As<ICommandBus>()
.SingleInstance();
container = containerBuilder.Build();
return new AutofacServiceProvider(container);
} }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
@ -57,18 +43,12 @@ namespace PinkParrot
app.UseMvc(); app.UseMvc();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseSwagger(); app.UseAppSwagger();
app.UseSwaggerUi();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
} }
} }
} }

9
src/PinkParrot/project.json

@ -1,4 +1,4 @@
{ {
"dependencies": { "dependencies": {
"Autofac": "4.1.0", "Autofac": "4.1.0",
"Autofac.Extensions.DependencyInjection": "4.0.0", "Autofac.Extensions.DependencyInjection": "4.0.0",
@ -27,8 +27,7 @@
"PinkParrot.Infrastructure": "1.0.0-*", "PinkParrot.Infrastructure": "1.0.0-*",
"PinkParrot.Read": "1.0.0-*", "PinkParrot.Read": "1.0.0-*",
"PinkParrot.Write": "1.0.0-*", "PinkParrot.Write": "1.0.0-*",
"Swashbuckle.SwaggerGen": "6.0.0-beta901", "Swashbuckle": "6.0.0-beta902"
"Swashbuckle.SwaggerUi": "6.0.0-beta901"
}, },
"tools": { "tools": {
@ -46,7 +45,9 @@
"buildOptions": { "buildOptions": {
"emitEntryPoint": true, "emitEntryPoint": true,
"preserveCompilationContext": true "preserveCompilationContext": true,
"xmlDoc": true,
"nowarn": [ "1591", "1573", "1572" ]
}, },
"runtimeOptions": { "runtimeOptions": {

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

@ -21,11 +21,13 @@ namespace PinkParrot.Core.Schema
AddFactory<NumberFieldProperties>(id => new NumberField(id)); AddFactory<NumberFieldProperties>(id => new NumberField(id));
} }
public void AddFactory<T>(Func<long, ModelField> factory) where T : ModelFieldProperties public ModelFieldFactory AddFactory<T>(Func<long, ModelField> factory) where T : ModelFieldProperties
{ {
Guard.NotNull(factory, nameof(factory)); Guard.NotNull(factory, nameof(factory));
factories[typeof(T)] = factory; factories[typeof(T)] = factory;
return this;
} }
public virtual ModelField CreateField(long id, ModelFieldProperties properties) public virtual ModelField CreateField(long id, ModelFieldProperties properties)

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

@ -64,22 +64,22 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(newMetadata, fields); return new ModelSchema(newMetadata, fields);
} }
public ModelSchema AddField(long id, ModelFieldProperties properties, ModelFieldFactory factory) public ModelSchema AddField(long id, ModelFieldProperties fieldProperties, ModelFieldFactory factory)
{ {
var field = factory.CreateField(id, properties); var field = factory.CreateField(id, fieldProperties);
return SetField(field); return SetField(field);
} }
public ModelSchema SetField(long fieldId, ModelFieldProperties properties) public ModelSchema SetField(long fieldId, ModelFieldProperties fieldProperties)
{ {
Guard.NotNull(properties, nameof(properties)); Guard.NotNull(fieldProperties, nameof(fieldProperties));
return UpdateField(fieldId, field => return UpdateField(fieldId, field =>
{ {
var errors = new List<ValidationError>(); var errors = new List<ValidationError>();
var newField = field.Configure(properties, errors); var newField = field.Configure(fieldProperties, errors);
if (errors.Any()) if (errors.Any())
{ {

2
src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaProperties.cs

@ -1,5 +1,5 @@
// ========================================================================== // ==========================================================================
// ModelSchemaMetadata.cs // ModelSchemaProperties.cs
// PinkParrot Headless CMS // PinkParrot Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) PinkParrot Group // Copyright (c) PinkParrot Group

2
src/pinkparrot_core/PinkParrot.Core/Schema/NamedElementProperties.cs

@ -7,6 +7,7 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema namespace PinkParrot.Core.Schema
@ -17,6 +18,7 @@ namespace PinkParrot.Core.Schema
private readonly string label; private readonly string label;
private readonly string hints; private readonly string hints;
[Required]
public string Name public string Name
{ {
get { return name; } get { return name; }

10
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Autofac/AutofacDomainObjectFactory.cs

@ -14,18 +14,18 @@ namespace PinkParrot.Infrastructure.CQRS.Autofac
{ {
public sealed class AutofacDomainObjectFactory : IDomainObjectFactory public sealed class AutofacDomainObjectFactory : IDomainObjectFactory
{ {
private readonly IContainer container; private readonly ILifetimeScope lifetimeScope;
public AutofacDomainObjectFactory(IContainer container) public AutofacDomainObjectFactory(ILifetimeScope lifetimeScope)
{ {
Guard.NotNull(container, nameof(container)); Guard.NotNull(lifetimeScope, nameof(lifetimeScope));
this.container = container; this.lifetimeScope = lifetimeScope;
} }
public IAggregate CreateNew(Type type, Guid id) public IAggregate CreateNew(Type type, Guid id)
{ {
return (IAggregate)container.Resolve(type, return (IAggregate)lifetimeScope.Resolve(type,
new NamedParameter("id", id), new NamedParameter("id", id),
new NamedParameter("version", 0)); new NamedParameter("version", 0));
} }

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/AggregateCommand.cs

@ -10,7 +10,7 @@ using System;
namespace PinkParrot.Infrastructure.CQRS.Commands namespace PinkParrot.Infrastructure.CQRS.Commands
{ {
public class AggregateCommand : ICommand public class AggregateCommand : IAggregateCommand
{ {
public Guid AggregateId { get; set; } public Guid AggregateId { get; set; }
} }

12
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/GetEventStore/GetEventStoreDomainObjectRepository.cs → src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/IAggregateCommand.cs

@ -1,13 +1,17 @@
// ========================================================================== // ==========================================================================
// GetEventStoreDomainObjectRepository.cs // IAggregateCommand.cs
// PinkParrot Headless CMS // PinkParrot Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) PinkParrot Group // Copyright (c) PinkParrot Group
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
namespace PinkParrot.Infrastructure.CQRS.GetEventStore
using System;
namespace PinkParrot.Infrastructure.CQRS.Commands
{ {
public class GetEventStoreDomainObjectRepository public interface IAggregateCommand : ICommand
{ {
Guid AggregateId { get; set; }
} }
} }

18
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Envelope.cs

@ -5,6 +5,7 @@
// Copyright (c) PinkParrot Group // Copyright (c) PinkParrot Group
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
namespace PinkParrot.Infrastructure.CQRS namespace PinkParrot.Infrastructure.CQRS
{ {
public class Envelope<TPayload> where TPayload : class public class Envelope<TPayload> where TPayload : class
@ -28,22 +29,23 @@ namespace PinkParrot.Infrastructure.CQRS
} }
} }
public Envelope(TPayload payload) public Envelope(TPayload payload, EnvelopeHeaders headers)
{ {
Guard.NotNull(payload, nameof(payload)); Guard.NotNull(payload, nameof(payload));
Guard.NotNull(headers, nameof(headers));
this.payload = payload; this.payload = payload;
this.headers = headers;
headers = new EnvelopeHeaders();
} }
public Envelope(TPayload payload, EnvelopeHeaders headers) public Envelope(TPayload payload)
: this(payload, new EnvelopeHeaders())
{ {
Guard.NotNull(payload, nameof(payload)); }
Guard.NotNull(headers, nameof(headers));
this.payload = payload; public Envelope(TPayload payload, PropertiesBag bag)
this.headers = headers; : this(payload, new EnvelopeHeaders(bag))
{
} }
public Envelope<TOther> To<TOther>() where TOther : class public Envelope<TOther> To<TOther>() where TOther : class

17
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeHeaders.cs

@ -9,6 +9,23 @@ namespace PinkParrot.Infrastructure.CQRS
{ {
public sealed class EnvelopeHeaders : PropertiesBag public sealed class EnvelopeHeaders : PropertiesBag
{ {
public EnvelopeHeaders()
{
}
public EnvelopeHeaders(PropertiesBag bag)
{
if (bag == null)
{
return;
}
foreach (var property in bag.Properties)
{
Set(property.Key, property.Value);
}
}
public EnvelopeHeaders Clone() public EnvelopeHeaders Clone()
{ {
var clone = new EnvelopeHeaders(); var clone = new EnvelopeHeaders();

33
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/DefaultNameResolver.cs

@ -0,0 +1,33 @@
// ==========================================================================
// DefaultNameResolver.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Globalization;
namespace PinkParrot.Infrastructure.CQRS.EventStore
{
public sealed class DefaultNameResolver : IStreamNameResolver
{
private readonly string prefix;
public DefaultNameResolver()
: this(string.Empty)
{
}
public DefaultNameResolver(string prefix)
{
this.prefix = prefix;
}
public string GetStreamName(Type aggregateType, Guid id)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}-{2}", prefix, char.ToLower(aggregateType.Name[0]) + aggregateType.Name.Substring(1), id);
}
}
}

153
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreDomainObjectRepository.cs

@ -0,0 +1,153 @@
// ==========================================================================
// GetEventStoreDomainObjectRepository.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using EventStore.ClientAPI;
using EventStore.ClientAPI.SystemData;
using PinkParrot.Infrastructure.CQRS.Commands;
// ReSharper disable RedundantAssignment
// ReSharper disable ConvertIfStatementToSwitchStatement
// ReSharper disable TooWideLocalVariableScope
namespace PinkParrot.Infrastructure.CQRS.EventStore
{
public sealed class EventStoreDomainObjectRepository : IDomainObjectRepository
{
private const int WritePageSize = 500;
private const int ReadPageSize = 500;
private readonly IEventStoreConnection connection;
private readonly IStreamNameResolver nameResolver;
private readonly IDomainObjectFactory factory;
private readonly UserCredentials credentials;
private readonly EventStoreParser formatter;
public EventStoreDomainObjectRepository(
IDomainObjectFactory factory,
IStreamNameResolver nameResolver,
IEventStoreConnection connection,
UserCredentials credentials,
EventStoreParser formatter)
{
Guard.NotNull(factory, nameof(factory));
Guard.NotNull(formatter, nameof(formatter));
Guard.NotNull(connection, nameof(connection));
Guard.NotNull(credentials, nameof(credentials));
Guard.NotNull(nameResolver, nameof(nameResolver));
this.factory = factory;
this.formatter = formatter;
this.connection = connection;
this.credentials = credentials;
this.nameResolver = nameResolver;
}
public async Task<TDomainObject> GetByIdAsync<TDomainObject>(Guid id, int version = 0) where TDomainObject : class, IAggregate
{
Guard.GreaterThan(version, 0, nameof(version));
var streamName = nameResolver.GetStreamName(typeof(TDomainObject), id);
var domainObject = (TDomainObject)factory.CreateNew(typeof(TDomainObject), id);
var sliceStart = 0;
var sliceCount = 0;
StreamEventsSlice currentSlice;
do
{
sliceCount = sliceStart + ReadPageSize <= version ? ReadPageSize : version - sliceStart + 1;
currentSlice = await connection.ReadStreamEventsForwardAsync(streamName, sliceStart, sliceCount, false, credentials);
if (currentSlice.Status == SliceReadStatus.StreamNotFound)
{
throw new DomainObjectNotFoundException(id.ToString(), typeof(TDomainObject));
}
if (currentSlice.Status == SliceReadStatus.StreamDeleted)
{
throw new DomainObjectDeletedException(id.ToString(), typeof(TDomainObject));
}
sliceStart = currentSlice.NextEventNumber;
foreach (var resolved in currentSlice.Events)
{
var envelope = formatter.Parse(resolved);
domainObject.ApplyEvent(envelope);
}
}
while (version >= currentSlice.NextEventNumber && !currentSlice.IsEndOfStream);
if (domainObject.Version != version && version < int.MaxValue)
{
throw new DomainObjectVersionException(id.ToString(), typeof(TDomainObject), domainObject.Version, version);
}
return domainObject;
}
public async Task SaveAsync(IAggregate domainObject, Guid commitId)
{
Guard.NotNull(domainObject, nameof(domainObject));
var streamName = nameResolver.GetStreamName(domainObject.GetType(), domainObject.Id);
var newEvents = domainObject.GetUncomittedEvents();
var currVersion = domainObject.Version;
var prevVersion = currVersion - newEvents.Count;
var exptVersion = prevVersion == 0 ? ExpectedVersion.NoStream : prevVersion - 1;
var eventsToSave = newEvents.Select(x => formatter.ToEventData(x, commitId)).ToList();
await InsertEventsAsync(streamName, exptVersion, eventsToSave);
domainObject.ClearUncommittedEvents();
}
private async Task InsertEventsAsync(string streamName, int exptVersion, IReadOnlyCollection<EventData> eventsToSave)
{
if (eventsToSave.Count > 0)
{
if (eventsToSave.Count < WritePageSize)
{
await connection.AppendToStreamAsync(streamName, exptVersion, eventsToSave, credentials);
}
else
{
var transaction = await connection.StartTransactionAsync(streamName, exptVersion, credentials);
try
{
for (var p = 0; p < eventsToSave.Count; p += WritePageSize)
{
await transaction.WriteAsync(eventsToSave.Skip(p).Take(WritePageSize));
}
await transaction.CommitAsync();
}
finally
{
transaction.Dispose();
}
}
}
else
{
Debug.WriteLine(string.Format("No events to insert for: {0}", streamName), "GetEventStoreRepository");
}
}
}
}

66
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreFormatter.cs

@ -0,0 +1,66 @@
// ==========================================================================
// EventStoreFormatter.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Text;
using EventStore.ClientAPI;
using Newtonsoft.Json;
using PinkParrot.Infrastructure.CQRS.Events;
// ReSharper disable InconsistentNaming
namespace PinkParrot.Infrastructure.CQRS.EventStore
{
public class EventStoreParser
{
private readonly JsonSerializerSettings serializerSettings;
public EventStoreParser(JsonSerializerSettings serializerSettings = null)
{
this.serializerSettings = serializerSettings ?? new JsonSerializerSettings();
}
public Envelope<IEvent> Parse(ResolvedEvent @event)
{
var headers = ReadJson<EnvelopeHeaders>(@event.Event.Metadata);
var eventType = Type.GetType(headers.Properties[CommonHeaders.EventType].ToString());
var eventData = ReadJson<IEvent>(@event.Event.Data, eventType);
var envelope = new Envelope<IEvent>(eventData, headers);
envelope.Headers.Set(CommonHeaders.Timestamp, DateTime.SpecifyKind(@event.Event.Created, DateTimeKind.Utc));
envelope.Headers.Set(CommonHeaders.EventNumber, @event.OriginalEventNumber);
return envelope;
}
public EventData ToEventData(Envelope<IEvent> envelope, Guid commitId)
{
var eventType = envelope.Payload.GetType();
envelope.Headers.Set(CommonHeaders.CommitId, commitId);
envelope.Headers.Set(CommonHeaders.EventType, eventType.AssemblyQualifiedName);
var headers = WriteJson(envelope.Headers);
var content = WriteJson(envelope.Payload);
return new EventData(envelope.Headers.EventId(), eventType.Name, true, content, headers);
}
private T ReadJson<T>(byte[] data, Type type = null)
{
return (T)JsonConvert.DeserializeObject(Encoding.UTF8.GetString(data), type ?? typeof(T), serializerSettings);
}
private byte[] WriteJson(object value)
{
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value, serializerSettings));
}
}
}

17
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/IStreamNameResolver.cs

@ -0,0 +1,17 @@
// ==========================================================================
// IStreamNameResolver.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Infrastructure.CQRS.EventStore
{
public interface IStreamNameResolver
{
string GetStreamName(Type aggregateType, Guid id);
}
}

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs

@ -25,7 +25,7 @@ namespace PinkParrot.Infrastructure.Dispatching
.Where(Helper.HasRightName) .Where(Helper.HasRightName)
.Where(Helper.HasRightParameters<TIn, TContext>) .Where(Helper.HasRightParameters<TIn, TContext>)
.Select(ActionContextDispatcherFactory.CreateActionHandler<TTarget, TContext>) .Select(ActionContextDispatcherFactory.CreateActionHandler<TTarget, TContext>)
.ToDictionary<Tuple<Type, Action<TTarget, object, TContext>>, Type, Action<TTarget, object, TContext>>(h => h.Item1, h => h.Item2); .ToDictionary(h => h.Item1, h => h.Item2);
} }
public static bool Dispatch(TTarget target, TIn input, TContext context) public static bool Dispatch(TTarget target, TIn input, TContext context)

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs

@ -25,7 +25,7 @@ namespace PinkParrot.Infrastructure.Dispatching
.Where(Helper.HasRightName) .Where(Helper.HasRightName)
.Where(Helper.HasRightParameters<TIn>) .Where(Helper.HasRightParameters<TIn>)
.Select(ActionDispatcherFactory.CreateActionHandler<TTarget>) .Select(ActionDispatcherFactory.CreateActionHandler<TTarget>)
.ToDictionary<Tuple<Type, Action<TTarget, object>>, Type, Action<TTarget, object>>(h => h.Item1, h => h.Item2); .ToDictionary(h => h.Item1, h => h.Item2);
} }
public static bool Dispatch(TTarget target, TIn item) public static bool Dispatch(TTarget target, TIn item)

9
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs

@ -26,19 +26,14 @@ namespace PinkParrot.Infrastructure.Dispatching
.Where(Helper.HasRightParameters<TIn, TContext>) .Where(Helper.HasRightParameters<TIn, TContext>)
.Where(Helper.HasRightReturnType<TOut>) .Where(Helper.HasRightReturnType<TOut>)
.Select(FuncContextDispatcherFactory.CreateFuncHandler<TTarget, TContext, TOut >) .Select(FuncContextDispatcherFactory.CreateFuncHandler<TTarget, TContext, TOut >)
.ToDictionary<Tuple<Type, Func<TTarget, object, TContext, TOut>>, Type, Func<TTarget, object, TContext, TOut>>(h => h.Item1, h => h.Item2); .ToDictionary(h => h.Item1, h => h.Item2);
} }
public static TOut Dispatch(TTarget target, TIn item, TContext context) public static TOut Dispatch(TTarget target, TIn item, TContext context)
{ {
Func<TTarget, object, TContext, TOut > handler; Func<TTarget, object, TContext, TOut > handler;
if (Handlers.TryGetValue(item.GetType(), out handler)) return Handlers.TryGetValue(item.GetType(), out handler) ? handler(target, item, context) : default(TOut);
{
return handler(target, item, context);
}
return default(TOut);
} }
} }
} }

9
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs

@ -26,19 +26,14 @@ namespace PinkParrot.Infrastructure.Dispatching
.Where(Helper.HasRightParameters<TIn>) .Where(Helper.HasRightParameters<TIn>)
.Where(Helper.HasRightReturnType<TOut>) .Where(Helper.HasRightReturnType<TOut>)
.Select(FuncDispatcherFactory.CreateFuncHandler<TTarget, TOut>) .Select(FuncDispatcherFactory.CreateFuncHandler<TTarget, TOut>)
.ToDictionary<Tuple<Type, Func<TTarget, object, TOut>>, Type, Func<TTarget, object, TOut>>(h => h.Item1, h => h.Item2); .ToDictionary(h => h.Item1, h => h.Item2);
} }
public static TOut Dispatch(TTarget target, TIn item) public static TOut Dispatch(TTarget target, TIn item)
{ {
Func<TTarget, object, TOut> handler; Func<TTarget, object, TOut> handler;
if (Handlers.TryGetValue(item.GetType(), out handler)) return Handlers.TryGetValue(item.GetType(), out handler) ? handler(target, item) : default(TOut);
{
return handler(target, item);
}
return default(TOut);
} }
} }
} }

25
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectDeletedException.cs

@ -0,0 +1,25 @@
// ==========================================================================
// DomainObjectDeletedException.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Infrastructure
{
public class DomainObjectDeletedException : DomainObjectException
{
public DomainObjectDeletedException(string id, Type type)
: base(FormatMessage(id, type), id, type)
{
}
private static string FormatMessage(string id, Type type)
{
return $"Domain object \'{id}\' (type {type}) not deleted.";
}
}
}

50
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectException.cs

@ -0,0 +1,50 @@
// ==========================================================================
// DomainObjectException.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Infrastructure
{
public class DomainObjectException : Exception
{
private readonly string id;
private readonly string typeName;
public string TypeName
{
get
{
return typeName;
}
}
public string Id
{
get
{
return id;
}
}
protected DomainObjectException(string message, string id, Type type)
: this(message, id, type, null)
{
}
protected DomainObjectException(string message, string id, Type type, Exception inner)
: base(message, inner)
{
this.id = id;
if (type != null)
{
typeName = type.Name;
}
}
}
}

25
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectNotFoundException.cs

@ -0,0 +1,25 @@
// ==========================================================================
// DomainObjectNotFoundException.cs
// Green Parrot Framework
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Infrastructure
{
public class DomainObjectNotFoundException : DomainObjectException
{
public DomainObjectNotFoundException(string id, Type type)
: base(FormatMessage(id, type), id, type)
{
}
private static string FormatMessage(string id, Type type)
{
return $"Domain object \'{id}\' (type {type}) not found.";
}
}
}

47
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainObjectVersionException.cs

@ -0,0 +1,47 @@
// ==========================================================================
// DomainObjectVersionException.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Infrastructure
{
public class DomainObjectVersionException : DomainObjectException
{
private readonly int currentVersion;
private readonly int expectedVersion;
public int CurrentVersion
{
get
{
return currentVersion;
}
}
public int ExpectedVersion
{
get
{
return expectedVersion;
}
}
public DomainObjectVersionException(string id, Type type, int currentVersion, int expectedVersion)
: base(FormatMessage(id, type, currentVersion, expectedVersion), id, type)
{
this.currentVersion = currentVersion;
this.expectedVersion = expectedVersion;
}
private static string FormatMessage(string id, Type type, int currentVersion, int expectedVersion)
{
return $"Request version {expectedVersion} for object '{id}' (type {type}), but found {currentVersion}.";
}
}
}

17
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/HideAttribute.cs

@ -0,0 +1,17 @@
// ==========================================================================
// HideAttribute.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Infrastructure
{
[AttributeUsage(AttributeTargets.Property)]
public class HideAttribute : Attribute
{
}
}

3
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/PropertiesBagConverter.cs

@ -7,6 +7,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
using NodaTime; using NodaTime;
using NodaTime.Extensions; using NodaTime.Extensions;
@ -17,7 +18,7 @@ namespace PinkParrot.Infrastructure.Json
{ {
public override bool CanConvert(Type objectType) public override bool CanConvert(Type objectType)
{ {
return objectType == typeof(PropertiesBag); return typeof(PropertiesBag).IsAssignableFrom(objectType);
} }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/PropertyValue.cs

@ -36,7 +36,7 @@ namespace PinkParrot.Infrastructure
{ typeof(Instant), (p, c) => p.ToInstant(c) }, { typeof(Instant), (p, c) => p.ToInstant(c) },
{ typeof(Instant?), (p, c) => p.ToNullableInstant(c) }, { typeof(Instant?), (p, c) => p.ToNullableInstant(c) },
{ typeof(Guid), (p, c) => p.ToGuid(c) }, { typeof(Guid), (p, c) => p.ToGuid(c) },
{ typeof(Guid?), (p, c) => p.ToNullableGuid(c) }, { typeof(Guid?), (p, c) => p.ToNullableGuid(c) }
}; };
public object RawValue public object RawValue

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

@ -2,9 +2,11 @@
"version": "1.0.0-*", "version": "1.0.0-*",
"dependencies": { "dependencies": {
"Autofac": "4.1.0", "Autofac": "4.1.0",
"EventStore.ClientAPI.DotNetCore": "1.0.0",
"NETStandard.Library": "1.6.0", "NETStandard.Library": "1.6.0",
"Newtonsoft.Json": "9.0.1", "Newtonsoft.Json": "9.0.1",
"NodaTime": "2.0.0-alpha20160729", "NodaTime": "2.0.0-alpha20160729",
"protobuf-net": "2.1.0",
"System.Linq": "4.1.0", "System.Linq": "4.1.0",
"System.Reflection.TypeExtensions": "4.1.0" "System.Reflection.TypeExtensions": "4.1.0"
}, },

5
src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaRM.cs

@ -7,15 +7,20 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.ComponentModel.DataAnnotations;
using PinkParrot.Infrastructure;
namespace PinkParrot.Read.Models namespace PinkParrot.Read.Models
{ {
public sealed class ModelSchemaRM public sealed class ModelSchemaRM
{ {
[Hide]
public string Id { get; set; } public string Id { get; set; }
[Required]
public string Name { get; set; } public string Name { get; set; }
[Required]
public Guid SchemaId { get; set; } public Guid SchemaId { get; set; }
} }
} }

18
src/pinkparrot_read/PinkParrot.Read/Services/ISchemaProvider.cs

@ -0,0 +1,18 @@
// ==========================================================================
// ISchemaProvider.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace PinkParrot.Read.Services
{
public interface ISchemaProvider
{
Task<Guid> FindSchemaIdByNameAsync(string name);
}
}

21
src/pinkparrot_read/PinkParrot.Read/Services/Implementations/SchemaProvider.cs

@ -0,0 +1,21 @@
// ==========================================================================
// SchemaProvider.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace PinkParrot.Read.Services.Implementations
{
public class SchemaProvider : ISchemaProvider
{
public Task<Guid> FindSchemaIdByNameAsync(string name)
{
return Task.FromResult(Guid.Empty);
}
}
}

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

@ -13,6 +13,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class AddModelField : AggregateCommand public class AddModelField : AggregateCommand
{ {
public ModelFieldProperties Properties; public ModelFieldProperties Properties { get; set; }
} }
} }

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

@ -13,6 +13,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class CreateModelSchema : AggregateCommand public class CreateModelSchema : AggregateCommand
{ {
public ModelSchemaProperties Properties; public ModelSchemaProperties Properties { get; set; }
} }
} }

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

@ -12,6 +12,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class DeleteModelField : AggregateCommand public class DeleteModelField : AggregateCommand
{ {
public long FieldId; public long FieldId { get; set; }
} }
} }

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

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

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

@ -12,6 +12,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class DisableModelField : AggregateCommand public class DisableModelField : AggregateCommand
{ {
public long FieldId; public long FieldId { get; set; }
} }
} }

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

@ -12,6 +12,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class EnableModelField : AggregateCommand public class EnableModelField : AggregateCommand
{ {
public long FieldId; public long FieldId { get; set; }
} }
} }

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

@ -12,6 +12,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class HideModelField : AggregateCommand public class HideModelField : AggregateCommand
{ {
public long FieldId; public long FieldId { get; set; }
} }
} }

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

@ -12,6 +12,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class ShowModelField : AggregateCommand public class ShowModelField : AggregateCommand
{ {
public long FieldId; public long FieldId { get; set; }
} }
} }

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

@ -13,8 +13,8 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class UpdateModelField : AggregateCommand public class UpdateModelField : AggregateCommand
{ {
public long FieldId; public long FieldId { get; set; }
public ModelFieldProperties Properties; public ModelFieldProperties Properties { get; set; }
} }
} }

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

@ -13,6 +13,6 @@ namespace PinkParrot.Write.Schema.Commands
{ {
public class UpdateModelSchema : AggregateCommand public class UpdateModelSchema : AggregateCommand
{ {
public ModelSchemaProperties Properties; public ModelSchemaProperties Properties { get; set; }
} }
} }

24
src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs

@ -21,61 +21,61 @@ namespace PinkParrot.Write.Schema
return this.DispatchActionAsync(context.Command, context); return this.DispatchActionAsync(context.Command, context);
} }
protected Task On(AddModelField command, CommandContext context) public Task On(AddModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.AddField(command)); return Update(command, context, schema => schema.AddField(command));
} }
protected Task On(DeleteModelField command, CommandContext context) public Task On(DeleteModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.DeleteField(command)); return Update(command, context, schema => schema.DeleteField(command));
} }
protected Task On(DeleteModelSchema command, CommandContext context) public Task On(DeleteModelSchema command, CommandContext context)
{ {
return Update(command, context, schema => schema.Delete(command)); return Update(command, context, schema => schema.Delete(command));
} }
protected Task On(DisableModelField command, CommandContext context) public Task On(DisableModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.DisableField(command)); return Update(command, context, schema => schema.DisableField(command));
} }
protected Task On(EnableModelField command, CommandContext context) public Task On(EnableModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.EnableField(command)); return Update(command, context, schema => schema.EnableField(command));
} }
protected Task On(HideModelField command, CommandContext context) public Task On(HideModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.HideField(command)); return Update(command, context, schema => schema.HideField(command));
} }
protected Task On(ShowModelField command, CommandContext context) public Task On(ShowModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.ShowField(command)); return Update(command, context, schema => schema.ShowField(command));
} }
protected Task On(UpdateModelField command, CommandContext context) public Task On(UpdateModelField command, CommandContext context)
{ {
return Update(command, context, schema => schema.UpdateField(command)); return Update(command, context, schema => schema.UpdateField(command));
} }
protected Task On(UpdateModelSchema command, CommandContext context) public Task On(UpdateModelSchema command, CommandContext context)
{ {
return Update(command, context, schema => schema.Update(command)); return Update(command, context, schema => schema.Update(command));
} }
protected Task On(CreateModelSchema command, CommandContext context) public Task On(CreateModelSchema command, CommandContext context)
{ {
var schema = context.Factory.CreateNew<ModelSchemaDomainObject>(command.AggregateId); var schema = context.Factory.CreateNew<ModelSchemaDomainObject>(command.AggregateId);
schema.Create(command); schema.Create(command);
return context.Repository.SaveAsync(schema, Guid.NewGuid()); return context.Repository.SaveAsync(schema, command.AggregateId);
} }
private async Task Update(AggregateCommand command, CommandContext context, Action<ModelSchemaDomainObject> updater) private static async Task Update(IAggregateCommand command, CommandContext context, Action<ModelSchemaDomainObject> updater)
{ {
var schema = await context.Repository.GetByIdAsync<ModelSchemaDomainObject>(command.AggregateId); var schema = await context.Repository.GetByIdAsync<ModelSchemaDomainObject>(command.AggregateId);

Loading…
Cancel
Save