Browse Source

Temporary commit to test another approach

pull/1/head
Sebastian Stehle 9 years ago
parent
commit
3690bff727
  1. 1
      src/PinkParrot/Configurations/Serializers.cs
  2. 44
      src/PinkParrot/Configurations/Swagger.cs
  3. 85
      src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs
  4. 55
      src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
  5. 29
      src/PinkParrot/Pipeline/Swagger/CamelCaseParameterFilter.cs
  6. 65
      src/PinkParrot/Pipeline/Swagger/HidePropertyFilter.cs
  7. 36
      src/PinkParrot/Pipeline/Swagger/RemoveReadonlyFilter.cs
  8. 12
      src/PinkParrot/Startup.cs
  9. 3
      src/PinkParrot/project.json
  10. 14
      src/pinkparrot_core/PinkParrot.Core/Schema/Json/SchemaDto.cs
  11. 2
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs
  12. 8
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelField_Generic.cs
  13. 58
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
  14. 6
      src/pinkparrot_core/PinkParrot.Core/Schema/NamedElementProperties.cs
  15. 6
      src/pinkparrot_core/PinkParrot.Core/Schema/NumberFieldProperties.cs
  16. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcher.cs
  17. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcher.cs
  18. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcher.cs
  19. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcher.cs
  20. 19
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/IValidatable.cs
  21. 1
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/SimpleMapper.cs
  22. 29
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ValidationExtensions.cs
  23. 3
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/EntityMapper.cs
  24. 17
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaRepository.cs
  25. 2
      src/pinkparrot_read/PinkParrot.Read/Services/Implementations/MongoStreamPositionsStorage.cs
  26. 1
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
  27. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
  28. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelSchema.cs
  29. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
  30. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
  31. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
  32. 1
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
  33. 1
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelSchema.cs
  34. 12
      src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs

1
src/PinkParrot/Configurations/Serializers.cs

@ -23,6 +23,7 @@ namespace PinkParrot.Configurations
settings.Binder = new TypeNameSerializationBinder().Map(typeof(ModelSchema).GetTypeInfo().Assembly); settings.Binder = new TypeNameSerializationBinder().Map(typeof(ModelSchema).GetTypeInfo().Assembly);
settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.Converters.Add(new PropertiesBagConverter()); settings.Converters.Add(new PropertiesBagConverter());
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
settings.DateParseHandling = DateParseHandling.DateTime; settings.DateParseHandling = DateParseHandling.DateTime;
settings.TypeNameHandling = TypeNameHandling.Auto; settings.TypeNameHandling = TypeNameHandling.Auto;

44
src/PinkParrot/Configurations/Swagger.cs

@ -1,44 +0,0 @@
// ==========================================================================
// 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");
}
}
}

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

@ -11,9 +11,6 @@ using Microsoft.AspNetCore.Mvc;
using PinkParrot.Core.Schema; 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
{ {
@ -24,14 +21,8 @@ namespace PinkParrot.Modules.Api.Schemas
{ {
} }
/// <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("api/schemas/{name}/fields/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Add(string name, [FromBody] ModelFieldProperties field) public Task Add(string name, [FromBody] ModelFieldProperties field)
{ {
var command = new AddModelField { Properties = field }; var command = new AddModelField { Properties = field };
@ -39,83 +30,57 @@ namespace PinkParrot.Modules.Api.Schemas
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("api/schemas/{name}/fields/{fieldId:long}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })] public Task Update(string name, long fieldId, [FromBody] ModelFieldProperties properties)
public Task Update(string name, long fieldId, [FromBody] UpdateModelField command)
{ {
var command = new UpdateModelField { FieldId = fieldId, Properties = properties };
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("api/schemas/{name}/fields/{fieldId:long}/hide/")]
[SwaggerOperation(Tags = new[] { "Schemas" })] public Task Hide(string name, long fieldId)
public Task Hide(string name, long fieldId, HideModelField command)
{ {
var command = new HideModelField { FieldId = fieldId };
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("api/schemas/{name}/fields/{fieldId:long}/show/")]
[SwaggerOperation(Tags = new[] { "Schemas" })] public Task Show(string name, long fieldId)
public Task Show(string name, long fieldId, ShowModelField command)
{ {
var command = new ShowModelField { FieldId = fieldId };
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("api/schemas/{name}/fields/{fieldId:long}/enable/")]
[SwaggerOperation(Tags = new[] { "Schemas" })] public Task Enable(string name, long fieldId)
public Task Enable(string name, long fieldId, EnableModelField command)
{ {
var command = new EnableModelField { FieldId = fieldId };
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("api/schemas/{name}/fields/{fieldId:long}/disable/")]
[SwaggerOperation(Tags = new[] { "Schemas" })] public Task Disable(string name, long fieldId)
public Task Disable(string name, long fieldId, DisableModelField command)
{ {
var command = new DisableModelField { FieldId = fieldId };
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("api/schemas/{name}/fields/{fieldId:long}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })] public Task Delete(string name, long fieldId)
public Task Delete(string name, long fieldId, DeleteModelField command)
{ {
var command = new DeleteModelField { FieldId = fieldId };
return CommandBus.PublishAsync(command); return CommandBus.PublishAsync(command);
} }
} }

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

@ -17,9 +17,6 @@ using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Infrastructure.Reflection; using PinkParrot.Infrastructure.Reflection;
using PinkParrot.Read.Repositories; using PinkParrot.Read.Repositories;
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
{ {
@ -33,13 +30,8 @@ namespace PinkParrot.Modules.Api.Schemas
this.modelSchemaRepository = modelSchemaRepository; this.modelSchemaRepository = modelSchemaRepository;
} }
/// <summary>
/// Queries all your schemas.
/// </summary>
[HttpGet] [HttpGet]
[Route("schemas/")] [Route("api/schemas/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(List<SchemasDto>), 200)]
public async Task<List<SchemasDto>> Query() public async Task<List<SchemasDto>> Query()
{ {
var schemas = await modelSchemaRepository.QueryAllAsync(TenantId); var schemas = await modelSchemaRepository.QueryAllAsync(TenantId);
@ -47,15 +39,8 @@ namespace PinkParrot.Modules.Api.Schemas
return schemas.Select(s => SimpleMapper.Map(s, new SchemasDto())).ToList(); return schemas.Select(s => SimpleMapper.Map(s, new SchemasDto())).ToList();
} }
/// <summary>
/// Gets the schema with the specified name.
/// </summary>
/// <param name="name">The name of the schema.</param>
/// <response code="404">Schema not found</response>
[HttpGet] [HttpGet]
[Route("schemas/{name}/")] [Route("api/schemas/{name}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(SchemaDto), 200)]
public async Task<ActionResult> Get(string name) public async Task<ActionResult> Get(string name)
{ {
var entity = await modelSchemaRepository.FindSchemaAsync(TenantId, name); var entity = await modelSchemaRepository.FindSchemaAsync(TenantId, name);
@ -68,19 +53,8 @@ namespace PinkParrot.Modules.Api.Schemas
return Ok(SchemaDto.Create(entity.Schema)); return Ok(SchemaDto.Create(entity.Schema));
} }
/// <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("api/schemas/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(EntityCreatedDto), 201)]
public async Task<ActionResult> Create([FromBody] ModelSchemaProperties schema) public async Task<ActionResult> Create([FromBody] ModelSchemaProperties schema)
{ {
var command = new CreateModelSchema { AggregateId = Guid.NewGuid(), Properties = schema }; var command = new CreateModelSchema { AggregateId = Guid.NewGuid(), Properties = schema };
@ -90,18 +64,8 @@ namespace PinkParrot.Modules.Api.Schemas
return CreatedAtAction("Query", new EntityCreatedDto { Id = command.AggregateId }); return CreatedAtAction("Query", new EntityCreatedDto { 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("api/schemas/{name}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(void), 204)]
public async Task<ActionResult> Update(string name, [FromBody] ModelSchemaProperties schema) public async Task<ActionResult> Update(string name, [FromBody] ModelSchemaProperties schema)
{ {
var command = new UpdateModelSchema { Properties = schema }; var command = new UpdateModelSchema { Properties = schema };
@ -111,17 +75,8 @@ namespace PinkParrot.Modules.Api.Schemas
return NoContent(); 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("api/schemas/{name}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(void), 204)]
public async Task<ActionResult> Delete(string name) public async Task<ActionResult> Delete(string name)
{ {
await CommandBus.PublishAsync(new DeleteModelSchema()); await CommandBus.PublishAsync(new DeleteModelSchema());

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

@ -1,29 +0,0 @@
// ==========================================================================
// 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

@ -1,65 +0,0 @@
// ==========================================================================
// 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

@ -1,36 +0,0 @@
// ==========================================================================
// 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);
}
}
}
}

12
src/PinkParrot/Startup.cs

@ -26,7 +26,6 @@ namespace PinkParrot
services.AddMvc().AddAppSerializers(); services.AddMvc().AddAppSerializers();
services.AddRouting(); services.AddRouting();
services.AddMemoryCache(); services.AddMemoryCache();
services.AddAppSwagger();
services.AddEventFormatter(); services.AddEventFormatter();
var builder = new ContainerBuilder(); var builder = new ContainerBuilder();
@ -42,16 +41,15 @@ namespace PinkParrot
{ {
loggerFactory.AddConsole(); loggerFactory.AddConsole();
app.UseAppTenants();
app.UseMvc();
app.UseStaticFiles();
app.UseAppSwagger();
app.UseAppEventBus();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
app.UseAppTenants();
app.UseMvc();
app.UseStaticFiles();
app.UseAppEventBus();
} }
} }
} }

3
src/PinkParrot/project.json

@ -26,8 +26,7 @@
"PinkParrot.Events": "1.0.0-*", "PinkParrot.Events": "1.0.0-*",
"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": "6.0.0-beta902"
}, },
"tools": { "tools": {

14
src/pinkparrot_core/PinkParrot.Core/Schema/Json/SchemaDto.cs

@ -7,8 +7,6 @@
// ========================================================================== // ==========================================================================
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
// ReSharper disable LoopCanBeConvertedToQuery // ReSharper disable LoopCanBeConvertedToQuery
@ -18,21 +16,19 @@ namespace PinkParrot.Core.Schema.Json
public class SchemaDto public class SchemaDto
{ {
private readonly ModelSchemaProperties properties; private readonly ModelSchemaProperties properties;
private readonly ImmutableList<FieldDto> fields; private readonly ImmutableDictionary<long, ModelFieldProperties> fields;
[Required] public ImmutableDictionary<long, ModelFieldProperties> Fields
public ImmutableList<FieldDto> Fields
{ {
get { return fields; } get { return fields; }
} }
[Required]
public ModelSchemaProperties Properties public ModelSchemaProperties Properties
{ {
get { return properties; } get { return properties; }
} }
public SchemaDto(ModelSchemaProperties properties, ImmutableList<FieldDto> fields) public SchemaDto(ModelSchemaProperties properties, ImmutableDictionary<long, ModelFieldProperties> fields)
{ {
Guard.NotNull(fields, nameof(fields)); Guard.NotNull(fields, nameof(fields));
Guard.NotNull(properties, nameof(properties)); Guard.NotNull(properties, nameof(properties));
@ -46,7 +42,7 @@ namespace PinkParrot.Core.Schema.Json
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
var fields = schema.Fields.Select(kvp => new FieldDto(kvp.Key, kvp.Value.RawProperties)).ToImmutableList(); var fields = schema.Fields.ToImmutableDictionary(x => x.Key, x => x.Value.RawProperties);
return new SchemaDto(schema.Properties, fields); return new SchemaDto(schema.Properties, fields);
} }
@ -59,7 +55,7 @@ namespace PinkParrot.Core.Schema.Json
foreach (var field in fields) foreach (var field in fields)
{ {
schema = schema.AddField(field.Id, field.Properties, factory); schema = schema.AddField(field.Key, field.Value, factory);
} }
return schema; return schema;

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

@ -54,7 +54,7 @@ namespace PinkParrot.Core.Schema
this.id = id; this.id = id;
} }
public abstract ModelField Configure(ModelFieldProperties newProperties, IList<ValidationError> errors); public abstract ModelField Configure(ModelFieldProperties newProperties);
public Task ValidateAsync(PropertyValue property, ICollection<string> errors) public Task ValidateAsync(PropertyValue property, ICollection<string> errors)
{ {

8
src/pinkparrot_core/PinkParrot.Core/Schema/ModelField_Generic.cs

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using PinkParrot.Infrastructure; using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema namespace PinkParrot.Core.Schema
@ -28,7 +27,7 @@ namespace PinkParrot.Core.Schema
public override string Label public override string Label
{ {
get { return properties.Label; } get { return properties.Label ?? properties.Name; }
} }
public override string Hints public override string Hints
@ -54,10 +53,9 @@ namespace PinkParrot.Core.Schema
this.properties = properties; this.properties = properties;
} }
public override ModelField Configure(ModelFieldProperties newProperties, IList<ValidationError> errors) public override ModelField Configure(ModelFieldProperties newProperties)
{ {
Guard.NotNull(newProperties, nameof(newProperties)); Guard.NotNull(newProperties, nameof(newProperties));
Guard.NotNull(errors, nameof(errors));
var typedProperties = newProperties as T; var typedProperties = newProperties as T;
@ -66,8 +64,6 @@ namespace PinkParrot.Core.Schema
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties));
} }
newProperties.Validate(errors);
return Update<ModelField<T>>(clone => clone.properties = typedProperties); return Update<ModelField<T>>(clone => clone.properties = typedProperties);
} }
} }

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

@ -32,22 +32,6 @@ namespace PinkParrot.Core.Schema
fieldsByName = fields.Values.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); fieldsByName = fields.Values.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
} }
public static ModelSchema Create(ModelSchemaProperties properties)
{
Guard.NotNull(properties, nameof(properties));
var errors = new List<ValidationError>();
properties.Validate(errors);
if (errors.Any())
{
throw new ValidationException("Failed to create a new model schema.", errors);
}
return new ModelSchema(properties, ImmutableDictionary<long, ModelField>.Empty);
}
public ImmutableDictionary<long, ModelField> Fields public ImmutableDictionary<long, ModelField> Fields
{ {
get { return fieldsById; } get { return fieldsById; }
@ -58,11 +42,22 @@ namespace PinkParrot.Core.Schema
get { return properties; } get { return properties; }
} }
public ModelSchema Update(ModelSchemaProperties newMetadata) public static ModelSchema Create(ModelSchemaProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));
newProperties.Validate(() => "Failed to create a new model schema.");
return new ModelSchema(newProperties, ImmutableDictionary<long, ModelField>.Empty);
}
public ModelSchema Update(ModelSchemaProperties newProperties)
{ {
Guard.NotNull(newMetadata, nameof(newMetadata)); Guard.NotNull(newProperties, nameof(newProperties));
return new ModelSchema(newMetadata, fieldsById); newProperties.Validate(() => "Failed to update the model schema.");
return new ModelSchema(newProperties, fieldsById);
} }
public ModelSchema AddField(long id, ModelFieldProperties fieldProperties, ModelFieldFactory factory) public ModelSchema AddField(long id, ModelFieldProperties fieldProperties, ModelFieldFactory factory)
@ -78,14 +73,9 @@ namespace PinkParrot.Core.Schema
return UpdateField(fieldId, field => return UpdateField(fieldId, field =>
{ {
var errors = new List<ValidationError>(); fieldProperties.Validate(() => $"Cannot update field with id '{fieldId}', becase the settings are invalid.");
var newField = field.Configure(fieldProperties, errors);
if (errors.Any()) var newField = field.Configure(fieldProperties);
{
throw new ValidationException($"Cannot update field with id '{fieldId}', becase the settings are invalid.", errors);
}
return newField; return newField;
}); });
@ -142,11 +132,10 @@ namespace PinkParrot.Core.Schema
return new ModelSchema(properties, fieldsById.SetItem(field.Id, field)); return new ModelSchema(properties, fieldsById.SetItem(field.Id, field));
} }
public async Task ValidateAsync(PropertiesBag data) public async Task ValidateAsync(PropertiesBag data, IList<ValidationError> errors)
{ {
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));
Guard.NotNull(errors, nameof(errors));
var errors = new List<ValidationError>();
foreach (var kvp in data.Properties) foreach (var kvp in data.Properties)
{ {
@ -156,21 +145,14 @@ namespace PinkParrot.Core.Schema
if (fieldsByName.TryGetValue(kvp.Key, out field)) if (fieldsByName.TryGetValue(kvp.Key, out field))
{ {
var newErrors = new List<string>(); await field.ValidateAsync(kvp.Value, fieldErrors);
await field.ValidateAsync(kvp.Value, newErrors);
} }
else else
{ {
fieldErrors.Add($"'{kvp.Key}' is not a known field"); fieldErrors.Add($"'{kvp.Key}' is not a known field");
} }
errors.AddRange(fieldErrors.Select(x => new ValidationError(x, kvp.Key))); fieldErrors.ForEach(error => errors.Add(new ValidationError(error, kvp.Key)));
}
if (errors.Any())
{
throw new ValidationException("The data is not valid.", errors);
} }
} }
} }

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

@ -7,18 +7,16 @@
// ========================================================================== // ==========================================================================
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
{ {
public abstract class NamedElementProperties public abstract class NamedElementProperties : IValidatable
{ {
private readonly string name; private readonly string name;
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; }
@ -26,7 +24,7 @@ namespace PinkParrot.Core.Schema
public string Label public string Label
{ {
get { return string.IsNullOrWhiteSpace(label) ? name : label; } get { return label; }
} }
public string Hints public string Hints

6
src/pinkparrot_core/PinkParrot.Core/Schema/NumberFieldProperties.cs

@ -48,8 +48,11 @@ namespace PinkParrot.Core.Schema
errors.Add(new ValidationError("MinValue cannot be larger than max value", "MinValue", "MaxValue")); errors.Add(new ValidationError("MinValue cannot be larger than max value", "MinValue", "MaxValue"));
} }
if (DefaultValue.HasValue) if (!DefaultValue.HasValue)
{ {
return;
}
if (MinValue.HasValue && DefaultValue.Value < MinValue.Value) if (MinValue.HasValue && DefaultValue.Value < MinValue.Value)
{ {
errors.Add(new ValidationError("DefaultValue must be larger than the min value.", "DefaultValue")); errors.Add(new ValidationError("DefaultValue must be larger than the min value.", "DefaultValue"));
@ -62,4 +65,3 @@ namespace PinkParrot.Core.Schema
} }
} }
} }
}

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)

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

@ -26,7 +26,7 @@ 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)

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

@ -26,7 +26,7 @@ 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)

19
src/pinkparrot_core/PinkParrot.Core/Schema/Json/FieldDto.cs → src/pinkparrot_infrastructure/PinkParrot.Infrastructure/IValidatable.cs

@ -1,24 +1,17 @@
// ========================================================================== // ==========================================================================
// FieldDto.cs // IValidatable.cs
// PinkParrot Headless CMS // PinkParrot Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) PinkParrot Group // Copyright (c) PinkParrot Group
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
namespace PinkParrot.Core.Schema.Json using System.Collections.Generic;
{
public class FieldDto
{
public long Id { get; }
public ModelFieldProperties Properties { get; }
public FieldDto(long id, ModelFieldProperties properties) namespace PinkParrot.Infrastructure
{ {
Id = id; public interface IValidatable
{
Properties = properties; void Validate(IList<ValidationError> errors);
}
} }
} }

1
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/SimpleMapper.cs

@ -10,7 +10,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection;
// ReSharper disable StaticMemberInGenericType // ReSharper disable StaticMemberInGenericType

29
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ValidationExtensions.cs

@ -0,0 +1,29 @@
// ==========================================================================
// ValidationExtensions.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
namespace PinkParrot.Infrastructure
{
public static class ValidationExtensions
{
public static void Validate(this IValidatable target, Func<string> message)
{
var errors = new List<ValidationError>();
target.Validate(errors);
if (errors.Any())
{
throw new ValidationException(message(), errors);
}
}
}
}

3
src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/EntityMapper.cs

@ -76,10 +76,9 @@ namespace PinkParrot.Read.Repositories.Implementations
} }
Update(entity, headers); Update(entity, headers);
updater(entity); updater(entity);
var result = await collection.ReplaceOneAsync(t => t.Id == entity.Id, entity); await collection.ReplaceOneAsync(t => t.Id == entity.Id, entity);
} }
} }
} }

17
src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaRepository.cs

@ -10,7 +10,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json; using Newtonsoft.Json;
using PinkParrot.Core.Schema; using PinkParrot.Core.Schema;
@ -21,24 +20,23 @@ using PinkParrot.Infrastructure.CQRS;
using PinkParrot.Infrastructure.CQRS.Events; using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.Dispatching; using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Infrastructure.MongoDb; using PinkParrot.Infrastructure.MongoDb;
using PinkParrot.Infrastructure.Tasks;
namespace PinkParrot.Read.Repositories.Implementations namespace PinkParrot.Read.Repositories.Implementations
{ {
public sealed class MongoModelSchemaRepository : MongoRepositoryBase<MongoModelSchemaEntity>, IModelSchemaRepository, ICatchEventConsumer public sealed class MongoModelSchemaRepository : MongoRepositoryBase<MongoModelSchemaEntity>, IModelSchemaRepository, ICatchEventConsumer
{ {
private readonly JsonSerializerSettings serializerSettings; private readonly JsonSerializerSettings serializerSettings;
private readonly ModelFieldFactory fieldFactory; private readonly ModelFieldFactory factory;
public MongoModelSchemaRepository(IMongoDatabase database, JsonSerializerSettings serializerSettings, ModelFieldFactory fieldFactory) public MongoModelSchemaRepository(IMongoDatabase database, JsonSerializerSettings serializerSettings, ModelFieldFactory factory)
: base(database) : base(database)
{ {
Guard.NotNull(serializerSettings, nameof(serializerSettings)); Guard.NotNull(serializerSettings, nameof(serializerSettings));
Guard.NotNull(fieldFactory, nameof(fieldFactory)); Guard.NotNull(factory, nameof(factory));
this.serializerSettings = serializerSettings; this.serializerSettings = serializerSettings;
this.fieldFactory = fieldFactory; this.factory = factory;
} }
protected override Task SetupCollectionAsync(IMongoCollection<MongoModelSchemaEntity> collection) protected override Task SetupCollectionAsync(IMongoCollection<MongoModelSchemaEntity> collection)
@ -87,7 +85,7 @@ namespace PinkParrot.Read.Repositories.Implementations
public Task On(ModelFieldAdded @event, EnvelopeHeaders headers) public Task On(ModelFieldAdded @event, EnvelopeHeaders headers)
{ {
return UpdateSchema(headers, s => s.AddField(@event.FieldId, @event.Properties, fieldFactory)); return UpdateSchema(headers, s => s.AddField(@event.FieldId, @event.Properties, factory));
} }
public Task On(ModelFieldDeleted @event, EnvelopeHeaders headers) public Task On(ModelFieldDeleted @event, EnvelopeHeaders headers)
@ -123,8 +121,11 @@ namespace PinkParrot.Read.Repositories.Implementations
public Task On(ModelSchemaUpdated @event, EnvelopeHeaders headers) public Task On(ModelSchemaUpdated @event, EnvelopeHeaders headers)
{ {
return Collection.UpdateAsync(headers, e => return Collection.UpdateAsync(headers, e =>
{
if (!string.IsNullOrWhiteSpace(@event.Properties.Name))
{ {
e.Name = @event.Properties.Name; e.Name = @event.Properties.Name;
}
UpdateSchema(e, s => s.Update(@event.Properties)); UpdateSchema(e, s => s.Update(@event.Properties));
}); });
@ -166,7 +167,7 @@ namespace PinkParrot.Read.Repositories.Implementations
private ModelSchema Deserialize(MongoModelSchemaEntity entity) private ModelSchema Deserialize(MongoModelSchemaEntity entity)
{ {
return entity?.Schema.ToJsonObject<SchemaDto>(serializerSettings).ToModelSchema(fieldFactory); return entity?.Schema.ToJsonObject<SchemaDto>(serializerSettings).ToModelSchema(factory);
} }
} }
} }

2
src/pinkparrot_read/PinkParrot.Read/Services/Implementations/MongoStreamPositionsStorage.cs

@ -26,7 +26,7 @@ namespace PinkParrot.Read.Services.Implementations
public int? ReadPosition() public int? ReadPosition()
{ {
var document = Collection.Find(t => t.Id == Id).FirstOrDefault<MongoPosition, MongoPosition>(); var document = Collection.Find(t => t.Id == Id).FirstOrDefault();
if (document == null) if (document == null)
{ {

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

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using PinkParrot.Core.Schema; using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {

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

@ -6,8 +6,6 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class DeleteModelField : TenantCommand public class DeleteModelField : TenantCommand

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

@ -6,8 +6,6 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class DeleteModelSchema : TenantCommand public class DeleteModelSchema : TenantCommand

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

@ -6,8 +6,6 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class EnableModelField : TenantCommand public class EnableModelField : TenantCommand

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

@ -6,8 +6,6 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class HideModelField : TenantCommand public class HideModelField : TenantCommand

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

@ -6,8 +6,6 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {
public class ShowModelField : TenantCommand public class ShowModelField : TenantCommand

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

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using PinkParrot.Core.Schema; using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {

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

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using PinkParrot.Core.Schema; using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Write.Schema.Commands namespace PinkParrot.Write.Schema.Commands
{ {

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

@ -17,9 +17,9 @@ using PinkParrot.Write.Schema.Commands;
namespace PinkParrot.Write.Schema namespace PinkParrot.Write.Schema
{ {
public class ModelSchemaDomainObject : DomainObject, IAggregate public class ModelSchemaDomainObject : DomainObject
{ {
private readonly ModelFieldFactory fieldFactory; private readonly ModelFieldFactory factory;
private Guid tenantId; private Guid tenantId;
private bool isDeleted; private bool isDeleted;
private long totalFields; private long totalFields;
@ -40,15 +40,15 @@ namespace PinkParrot.Write.Schema
get { return isDeleted; } get { return isDeleted; }
} }
public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory fieldFactory) public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory factory)
: base(id, version) : base(id, version)
{ {
this.fieldFactory = fieldFactory; this.factory = factory;
} }
public void On(ModelFieldAdded @event) public void On(ModelFieldAdded @event)
{ {
schema = schema.AddField(@event.FieldId, @event.Properties, fieldFactory); schema = schema.AddField(@event.FieldId, @event.Properties, factory);
totalFields++; totalFields++;
} }
@ -106,7 +106,7 @@ namespace PinkParrot.Write.Schema
var id = ++totalFields; var id = ++totalFields;
schema = schema.AddField(id, command.Properties, fieldFactory); schema = schema.AddField(id, command.Properties, factory);
RaiseEvent(new ModelFieldAdded { FieldId = id, Properties = command.Properties }, true); RaiseEvent(new ModelFieldAdded { FieldId = id, Properties = command.Properties }, true);
} }

Loading…
Cancel
Save