Browse Source

test

pull/1/head
Sebastian Stehle 9 years ago
parent
commit
1962e4fca8
  1. 5
      src/PinkParrot/Configurations/Serializers.cs
  2. 2
      src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
  3. 16
      src/pinkparrot_core/PinkParrot.Core/Schema/IModelFieldProperties.cs
  4. 19
      src/pinkparrot_core/PinkParrot.Core/Schema/IRegisterModelField.cs
  5. 64
      src/pinkparrot_core/PinkParrot.Core/Schema/Json/SchemaDto.cs
  6. 42
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs
  7. 49
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldFactory.cs
  8. 16
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldProperties.cs
  9. 97
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldRegistry.cs
  10. 19
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelField_Generic.cs
  11. 84
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs
  12. 3
      src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaProperties.cs
  13. 31
      src/pinkparrot_core/PinkParrot.Core/Schema/NamedElementProperties.cs
  14. 15
      src/pinkparrot_core/PinkParrot.Core/Schema/NumberField.cs
  15. 18
      src/pinkparrot_core/PinkParrot.Core/Schema/NumberFieldProperties.cs
  16. 6
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs
  17. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs
  18. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs
  19. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs
  20. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs
  21. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs
  22. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs
  23. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs
  24. 4
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs
  25. 2
      src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs
  26. 10
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandContext.cs
  27. 1
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/CommonHeaders.cs
  28. 7
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreFormatter.cs
  29. 35
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/TypeNameSerializationBinder.cs
  30. 75
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/TypeNameRegistry.cs
  31. 27
      src/pinkparrot_read/PinkParrot.Read/Models/ModelFieldDto.cs
  32. 63
      src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaDto.cs
  33. 4
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/EntityMapper.cs
  34. 2
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoModelSchemaEntity.cs
  35. 47
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoModelSchemaRepository.cs
  36. 2
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoPositions.cs
  37. 2
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoStreamPositionsStorage.cs
  38. 8
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/AddModelField.cs
  39. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/CreateModelSchema.cs
  40. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DeleteModelField.cs
  41. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/DisableModelField.cs
  42. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/EnableModelField.cs
  43. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/HideModelField.cs
  44. 2
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/ShowModelField.cs
  45. 6
      src/pinkparrot_write/PinkParrot.Write/Schema/Commands/UpdateModelField.cs
  46. 66
      src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaCommandHandler.cs
  47. 79
      src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs

5
src/PinkParrot/Configurations/Serializers.cs

@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using PinkParrot.Core.Schema;
using PinkParrot.Core.Schema.Json;
using PinkParrot.Infrastructure.CQRS.EventStore;
using PinkParrot.Infrastructure.Json;
@ -38,10 +39,8 @@ namespace PinkParrot.Configurations
public static void AddEventFormatter(this IServiceCollection services)
{
var fieldFactory = new ModelFieldFactory();
services.AddSingleton(t => CreateSettings());
services.AddSingleton(fieldFactory);
services.AddSingleton<SerializationService>();
services.AddSingleton<EventStoreFormatter>();
}

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

@ -50,7 +50,7 @@ namespace PinkParrot.Modules.Api.Schemas
return NotFound();
}
return Ok(SchemaDto.Create(entity.Schema));
return Ok(ModelSchemaDto.Create(entity.Schema));
}
[HttpPost]

16
src/pinkparrot_core/PinkParrot.Core/Schema/IModelFieldProperties.cs

@ -0,0 +1,16 @@
// ==========================================================================
// IModelFieldProperties.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema
{
public interface IModelFieldProperties : IValidatable
{
}
}

19
src/pinkparrot_core/PinkParrot.Core/Schema/IRegisterModelField.cs

@ -0,0 +1,19 @@
// ==========================================================================
// IRegisterModelField.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Core.Schema
{
public interface IRegisterModelField
{
Type PropertiesType { get; }
ModelField CreateField(long id, string name, IModelFieldProperties properties);
}
}

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

@ -1,64 +0,0 @@
// ==========================================================================
// SerializationModel.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.Collections.Immutable;
using PinkParrot.Infrastructure;
// ReSharper disable LoopCanBeConvertedToQuery
namespace PinkParrot.Core.Schema.Json
{
public class SchemaDto
{
private readonly ModelSchemaProperties properties;
private readonly ImmutableDictionary<long, ModelFieldProperties> fields;
public ImmutableDictionary<long, ModelFieldProperties> Fields
{
get { return fields; }
}
public ModelSchemaProperties Properties
{
get { return properties; }
}
public SchemaDto(ModelSchemaProperties properties, ImmutableDictionary<long, ModelFieldProperties> fields)
{
Guard.NotNull(fields, nameof(fields));
Guard.NotNull(properties, nameof(properties));
this.properties = properties;
this.fields = fields;
}
public static SchemaDto Create(ModelSchema schema)
{
Guard.NotNull(schema, nameof(schema));
var fields = schema.Fields.ToImmutableDictionary(x => x.Key, x => x.Value.RawProperties);
return new SchemaDto(schema.Properties, fields);
}
public ModelSchema ToModelSchema(ModelFieldFactory factory)
{
Guard.NotNull(factory, nameof(factory));
var schema = ModelSchema.Create(properties);
foreach (var field in fields)
{
schema = schema.AddField(field.Key, field.Value, factory);
}
return schema;
}
}
}

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

@ -12,6 +12,7 @@ using System.Threading.Tasks;
using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.Tasks;
// ReSharper disable InvertIf
// ReSharper disable ConvertIfStatementToReturnStatement
namespace PinkParrot.Core.Schema
@ -19,6 +20,7 @@ namespace PinkParrot.Core.Schema
public abstract class ModelField
{
private readonly long id;
private string name;
private bool isDisabled;
private bool isHidden;
@ -27,15 +29,10 @@ namespace PinkParrot.Core.Schema
get { return id; }
}
public abstract ModelFieldProperties RawProperties { get; }
public abstract string Name { get; }
public abstract string Label { get; }
public abstract string Hints { get; }
public abstract bool IsRequired { get; }
public string Name
{
get { return name; }
}
public bool IsHidden
{
@ -47,14 +44,25 @@ namespace PinkParrot.Core.Schema
get { return isDisabled; }
}
protected ModelField(long id)
public abstract IModelFieldProperties RawProperties { get; }
public abstract string Label { get; }
public abstract string Hints { get; }
public abstract bool IsRequired { get; }
protected ModelField(long id, string name)
{
Guard.GreaterThan(id, 0, nameof(id));
Guard.ValidSlug(name, nameof(name));
this.id = id;
this.name = name;
}
public abstract ModelField Configure(ModelFieldProperties newProperties);
public abstract ModelField Update(IModelFieldProperties newProperties);
public Task ValidateAsync(PropertyValue property, ICollection<string> errors)
{
@ -98,6 +106,18 @@ namespace PinkParrot.Core.Schema
return Update<ModelField>(clone => clone.isDisabled = false);
}
public ModelField Rename(string newName)
{
if (!newName.IsSlug())
{
var error = new ValidationError("Name must be a valid slug", "Name");
throw new ValidationException($"Cannot rename the field '{name}' ({id})", error);
}
return Update<ModelField>(clone => clone.name = newName);
}
protected T Update<T>(Action<T> updater) where T : ModelField
{
var clone = (T)Clone();

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

@ -1,49 +0,0 @@
// ==========================================================================
// ModelFieldFactory.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema
{
public class ModelFieldFactory
{
private readonly Dictionary<Type, Func<long, ModelFieldProperties, ModelField>> factories
= new Dictionary<Type, Func<long, ModelFieldProperties, ModelField>>();
public ModelFieldFactory()
{
AddFactory<NumberFieldProperties>((id, p) => new NumberField(id, (NumberFieldProperties)p));
}
public ModelFieldFactory AddFactory<T>(Func<long, ModelFieldProperties, ModelField> factory) where T : ModelFieldProperties
{
Guard.NotNull(factory, nameof(factory));
factories[typeof(T)] = factory;
return this;
}
public virtual ModelField CreateField(long id, ModelFieldProperties properties)
{
Guard.NotNull(properties, nameof(properties));
var factory = factories.GetOrDefault(properties.GetType());
if (factory == null)
{
throw new InvalidOperationException("Field type is not supported.");
}
return factory(id, properties);
}
}
}

16
src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldProperties.cs

@ -11,24 +11,18 @@ using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema
{
public abstract class ModelFieldProperties : NamedElementProperties
public abstract class ModelFieldProperties : NamedElementProperties, IModelFieldProperties
{
public bool IsRequired { get; }
protected ModelFieldProperties(
bool isRequired,
string name,
string label,
string hints)
: base(name, label, hints)
protected ModelFieldProperties(string label, string hints, bool isRequired)
: base(label, hints)
{
IsRequired = isRequired;
}
public override void Validate(IList<ValidationError> errors)
{
base.Validate(errors);
public void Validate(IList<ValidationError> errors)
{
ValidateCore(errors);
}

97
src/pinkparrot_core/PinkParrot.Core/Schema/ModelFieldRegistry.cs

@ -0,0 +1,97 @@
// ==========================================================================
// ModelFieldRegistry.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema
{
public delegate ModelField FactoryFunction(long id, string name, IModelFieldProperties properties);
public sealed class ModelFieldRegistry
{
private readonly Dictionary<string, IRegisterModelField> fieldsByTypeName = new Dictionary<string, IRegisterModelField>();
private readonly Dictionary<Type, IRegisterModelField> fieldsByPropertyType = new Dictionary<Type, IRegisterModelField>();
private sealed class Registered : IRegisterModelField
{
private readonly FactoryFunction fieldFactory;
private readonly Type propertiesType;
private readonly string typeName;
public Type PropertiesType
{
get { return propertiesType; }
}
public string TypeName
{
get { return typeName; }
}
public Registered(FactoryFunction fieldFactory, Type propertiesType)
{
typeName = TypeNameRegistry.GetName(propertiesType);
this.fieldFactory = fieldFactory;
this.propertiesType = propertiesType;
}
ModelField IRegisterModelField.CreateField(long id, string name, IModelFieldProperties properties)
{
return fieldFactory(id, name, properties);
}
}
public void Add<TFieldProperties>(FactoryFunction fieldFactory)
{
Guard.NotNull(fieldFactory, nameof(fieldFactory));
var registered = new Registered(fieldFactory, typeof(TFieldProperties));
fieldsByTypeName[registered.TypeName] = registered;
fieldsByPropertyType[registered.PropertiesType] = registered;
}
public ModelField CreateField(long id, string name, IModelFieldProperties properties)
{
var registered = fieldsByPropertyType[properties.GetType()];
return registered.CreateField(id, name, properties);
}
public IRegisterModelField FindByPropertiesType(Type type)
{
Guard.NotNull(type, nameof(type));
var registered = fieldsByPropertyType.GetOrDefault(type);
if (registered == null)
{
throw new InvalidOperationException($"The field property '{type}' is not supported.");
}
return registered;
}
public IRegisterModelField FindByTypeName(string typeName)
{
Guard.NotNullOrEmpty(typeName, nameof(typeName));
var registered = fieldsByTypeName.GetOrDefault(typeName);
if (registered == null)
{
throw new DomainException($"A field with type '{typeName} is not known.");
}
return registered;
}
}
}

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

@ -15,19 +15,14 @@ namespace PinkParrot.Core.Schema
{
private T properties;
public override ModelFieldProperties RawProperties
public override IModelFieldProperties RawProperties
{
get { return properties; }
}
public override string Name
{
get { return properties.Name; }
}
public override string Label
{
get { return properties.Label ?? properties.Name; }
get { return properties.Label ?? Name; }
}
public override string Hints
@ -45,18 +40,18 @@ namespace PinkParrot.Core.Schema
get { return properties; }
}
protected ModelField(long id, T properties)
: base(id)
protected ModelField(long id, string name, T properties)
: base(id, name)
{
Guard.NotNull(properties, nameof(properties));
this.properties = properties;
}
public override ModelField Configure(ModelFieldProperties newProperties)
public override ModelField Update(IModelFieldProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));
var typedProperties = newProperties as T;
if (typedProperties == null)
@ -64,6 +59,8 @@ namespace PinkParrot.Core.Schema
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties));
}
newProperties.Validate(() => $"Cannot update field with id '{Id}', becase the settings are invalid.");
return Update<ModelField<T>>(clone => clone.properties = typedProperties);
}
}

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

@ -12,24 +12,20 @@ using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using PinkParrot.Infrastructure;
// ReSharper disable InvertIf
namespace PinkParrot.Core.Schema
{
public sealed class ModelSchema
{
private readonly string name;
private readonly ModelSchemaProperties properties;
private readonly ImmutableDictionary<long, ModelField> fieldsById;
private readonly Dictionary<string, ModelField> fieldsByName;
public ModelSchema(ModelSchemaProperties properties, ImmutableDictionary<long, ModelField> fields)
public string Name
{
Guard.NotNull(fields, nameof(fields));
Guard.NotNull(properties, nameof(properties));
this.properties = properties;
fieldsById = fields;
fieldsByName = fields.Values.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
get { return name; }
}
public ImmutableDictionary<long, ModelField> Fields
@ -42,43 +38,56 @@ namespace PinkParrot.Core.Schema
get { return properties; }
}
public static ModelSchema Create(ModelSchemaProperties newProperties)
public ModelSchema(string name, ModelSchemaProperties properties, ImmutableDictionary<long, ModelField> fields)
{
Guard.NotNull(newProperties, nameof(newProperties));
Guard.NotNull(fields, nameof(fields));
Guard.NotNull(properties, nameof(properties));
Guard.ValidSlug(name, nameof(name));
this.name = name;
newProperties.Validate(() => "Failed to create a new model schema.");
this.properties = properties;
return new ModelSchema(newProperties, ImmutableDictionary<long, ModelField>.Empty);
fieldsById = fields;
fieldsByName = fields.Values.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
}
public ModelSchema Update(ModelSchemaProperties newProperties)
public static ModelSchema Create(string name, ModelSchemaProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));
newProperties.Validate(() => "Failed to update the model schema.");
if (!name.IsSlug())
{
var error = new ValidationError("Name must be a valid slug", "Name");
throw new ValidationException($"Cannot rename the schema '{name}'", error);
}
return new ModelSchema(newProperties, fieldsById);
return new ModelSchema(name, newProperties, ImmutableDictionary<long, ModelField>.Empty);
}
public ModelSchema AddField(long id, ModelFieldProperties fieldProperties, ModelFieldFactory factory)
public ModelSchema Update(ModelSchemaProperties newProperties)
{
var field = factory.CreateField(id, fieldProperties);
Guard.NotNull(newProperties, nameof(newProperties));
return ReplaceOrAddField(field);
return new ModelSchema(name, newProperties, fieldsById);
}
public ModelSchema SetField(long fieldId, ModelFieldProperties fieldProperties)
public ModelSchema AddOrUpdateField(ModelField field)
{
Guard.NotNull(fieldProperties, nameof(fieldProperties));
Guard.NotNull(field, nameof(field));
return UpdateField(fieldId, field =>
if (fieldsById.Values.Any(f => f.Name == field.Name && f.Id != field.Id))
{
fieldProperties.Validate(() => $"Cannot update field with id '{fieldId}', becase the settings are invalid.");
throw new ValidationException($"A field with name '{field.Name}' already exists.");
}
var newField = field.Configure(fieldProperties);
return new ModelSchema(name, properties, fieldsById.SetItem(field.Id, field));
}
return newField;
});
public ModelSchema UpdateField(long fieldId, IModelFieldProperties newProperties)
{
return UpdateField(fieldId, field => field.Update(newProperties));
}
public ModelSchema DisableField(long fieldId)
@ -101,13 +110,20 @@ namespace PinkParrot.Core.Schema
return UpdateField(fieldId, field => field.Show());
}
public ModelSchema RenameField(long fieldId, string newName)
{
return UpdateField(fieldId, field => field.Rename(newName));
}
public ModelSchema DeleteField(long fieldId)
{
return new ModelSchema(properties, fieldsById.Remove(fieldId));
return new ModelSchema(name, properties, fieldsById.Remove(fieldId));
}
private ModelSchema UpdateField(long fieldId, Func<ModelField, ModelField> updater)
public ModelSchema UpdateField(long fieldId, Func<ModelField, ModelField> updater)
{
Guard.NotNull(updater, nameof(updater));
ModelField field;
if (!fieldsById.TryGetValue(fieldId, out field))
@ -117,19 +133,7 @@ namespace PinkParrot.Core.Schema
var newField = updater(field);
return ReplaceOrAddField(newField);
}
private ModelSchema ReplaceOrAddField(ModelField field)
{
Guard.NotNull(field, nameof(field));
if (fieldsById.Values.Any(f => f.Name == field.Name && f.Id != field.Id))
{
throw new ValidationException($"A field with name '{field.Name}' already exists.");
}
return new ModelSchema(properties, fieldsById.SetItem(field.Id, field));
return AddOrUpdateField(newField);
}
public async Task ValidateAsync(PropertiesBag data, IList<ValidationError> errors)

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

@ -10,10 +10,9 @@ namespace PinkParrot.Core.Schema
public sealed class ModelSchemaProperties : NamedElementProperties
{
public ModelSchemaProperties(
string name,
string label,
string hints)
: base(name, label, hints)
: base(label, hints)
{
}
}

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

@ -6,21 +6,12 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema
{
public abstract class NamedElementProperties : IValidatable
public abstract class NamedElementProperties
{
private readonly string name;
private readonly string label;
private readonly string hints;
public string Name
{
get { return name; }
}
public string Label
{
@ -32,28 +23,10 @@ namespace PinkParrot.Core.Schema
get { return hints; }
}
protected NamedElementProperties(
string name,
string label,
string hints)
protected NamedElementProperties(string label, string hints)
{
this.name = name;
this.label = label;
this.hints = hints;
}
public virtual void Validate(IList<ValidationError> errors)
{
Guard.NotNull(errors, nameof(errors));
if (string.IsNullOrWhiteSpace(Name))
{
errors.Add(new ValidationError("Name cannot be empty.", "Name"));
}
else if (!Name.IsSlug())
{
errors.Add(new ValidationError("Name must be a valid slug.", "Name"));
}
}
}
}

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

@ -9,6 +9,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.Tasks;
@ -27,8 +28,13 @@ namespace PinkParrot.Core.Schema
get { return Properties.MinValue; }
}
public NumberField(long id, NumberFieldProperties properties)
: base(id, properties)
public double[] AllowedValues
{
get { return Properties.AllowedValues; }
}
public NumberField(long id, string name, NumberFieldProperties properties)
: base(id, name, properties)
{
}
@ -47,6 +53,11 @@ namespace PinkParrot.Core.Schema
{
errors.Add($"Must be less than {MaxValue}");
}
if (AllowedValues != null && !AllowedValues.Contains(value))
{
errors.Add($"Can only be one of the following value: {string.Join(", ", AllowedValues)}");
}
}
catch (InvalidCastException)
{

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

@ -11,39 +11,43 @@ using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schema
{
[TypeName("Number")]
[TypeName("number")]
public sealed class NumberFieldProperties : ModelFieldProperties
{
public string Placeholder { get; set; }
public double? DefaultValue { get; }
public double? MaxValue { get; }
public double? MinValue { get; }
public string Placeholder { get; set; }
public double[] AllowedValues { get; }
public NumberFieldProperties(
bool isRequired,
string name,
string label,
string hints,
string placeholder,
double? minValue,
double? maxValue,
double? defaultValue)
: base(isRequired, name, label, hints)
double? defaultValue,
double[] allowedValues,
bool isRequired)
: base(label, hints, isRequired)
{
Placeholder = placeholder;
MinValue = minValue;
MaxValue = maxValue;
AllowedValues = allowedValues;
DefaultValue = defaultValue;
}
protected override void ValidateCore(IList<ValidationError> errors)
{
if (MaxValue.HasValue && MinValue.HasValue)
if (MaxValue.HasValue && MinValue.HasValue && MinValue.Value >= MaxValue.Value)
{
errors.Add(new ValidationError("MinValue cannot be larger than max value", "MinValue", "MaxValue"));
}

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

@ -7,13 +7,17 @@
// ==========================================================================
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldAddedEvent")]
public class ModelFieldAdded : TenantEvent
{
public long FieldId;
public ModelFieldProperties Properties;
public string Name;
public IModelFieldProperties Properties;
}
}

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

@ -5,8 +5,12 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldDeletedEvent")]
public class ModelFieldDeleted : TenantEvent
{
public long FieldId;

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

@ -5,8 +5,12 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldDisabledEvent")]
public class ModelFieldDisabled : TenantEvent
{
public long FieldId;

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

@ -5,8 +5,12 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldEnabledEvent")]
public class ModelFieldEnabled : TenantEvent
{
public long FieldId;

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

@ -5,8 +5,12 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldHiddenEvent")]
public class ModelFieldHidden : TenantEvent
{
public long FieldId;

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

@ -5,8 +5,12 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldShownEvent")]
public class ModelFieldShown : TenantEvent
{
public long FieldId;

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

@ -7,13 +7,15 @@
// ==========================================================================
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelFieldUpdatedEvent")]
public class ModelFieldUpdated : TenantEvent
{
public long FieldId;
public ModelFieldProperties Properties;
public IModelFieldProperties Properties;
}
}

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

@ -7,11 +7,15 @@
// ==========================================================================
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelSchemaCreatedEvent")]
public class ModelSchemaCreated : TenantEvent
{
public string Name;
public ModelSchemaProperties Properties;
}
}

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

@ -5,8 +5,12 @@
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelSchemaDeleted")]
public class ModelSchemaDeleted : TenantEvent
{
}

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

@ -7,9 +7,11 @@
// ==========================================================================
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure;
namespace PinkParrot.Events.Schema
{
[TypeName("ModelSchemaUpdated")]
public class ModelSchemaUpdated : TenantEvent
{
public ModelSchemaProperties Properties;

10
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Commands/CommandContext.cs

@ -17,10 +17,10 @@ namespace PinkParrot.Infrastructure.CQRS.Commands
private readonly ICommand command;
private Exception exception;
private bool isSucceeded;
public ICommand Command
public IDomainObjectRepository Repository
{
get { return command; }
get { return repository; }
}
public IDomainObjectFactory Factory
@ -28,9 +28,9 @@ namespace PinkParrot.Infrastructure.CQRS.Commands
get { return factory; }
}
public IDomainObjectRepository Repository
public ICommand Command
{
get { return repository; }
get { return command; }
}
public bool IsHandled

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

@ -14,7 +14,6 @@ namespace PinkParrot.Infrastructure.CQRS
public const string Timestamp = "Timestamp";
public const string TenantId = "TenantId";
public const string EventId = "EventId";
public const string EventType = "EventType";
public const string EventNumber = "EventNumber";
}
}

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

@ -30,7 +30,7 @@ namespace PinkParrot.Infrastructure.CQRS.EventStore
{
var headers = ReadJson<PropertiesBag>(@event.Event.Metadata);
var eventType = Type.GetType(headers.Properties[CommonHeaders.EventType].ToString());
var eventType = TypeNameRegistry.GetType(@event.Event.EventType);
var eventData = ReadJson<IEvent>(@event.Event.Data, eventType);
var envelope = new Envelope<IEvent>(eventData, headers);
@ -43,15 +43,14 @@ namespace PinkParrot.Infrastructure.CQRS.EventStore
public EventData ToEventData(Envelope<IEvent> envelope, Guid commitId)
{
var eventType = envelope.Payload.GetType();
var eventType = TypeNameRegistry.GetName(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);
return new EventData(envelope.Headers.EventId(), eventType, true, content, headers);
}
private T ReadJson<T>(byte[] data, Type type = null)

35
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Json/TypeNameSerializationBinder.cs

@ -7,46 +7,15 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json.Serialization;
namespace PinkParrot.Infrastructure.Json
{
public class TypeNameSerializationBinder : DefaultSerializationBinder
{
private readonly Dictionary<Type, string> namesByType = new Dictionary<Type, string>();
private readonly Dictionary<string, Type> typesByName = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public TypeNameSerializationBinder Map(Type type, string name)
{
Guard.NotNull(type, nameof(type));
Guard.NotNull(name, nameof(name));
namesByType.Add(type, name);
typesByName.Add(name, type);
return this;
}
public TypeNameSerializationBinder Map(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var typeNameAttribute = type.GetTypeInfo().GetCustomAttribute<TypeNameAttribute>();
if (!string.IsNullOrWhiteSpace(typeNameAttribute?.TypeName))
{
Map(type, typeNameAttribute.TypeName);
}
}
return this;
}
public override Type BindToType(string assemblyName, string typeName)
{
var type = typesByName.GetOrDefault(typeName);
var type = TypeNameRegistry.GetType(typeName);
return type ?? base.BindToType(assemblyName, typeName);
}
@ -55,7 +24,7 @@ namespace PinkParrot.Infrastructure.Json
{
assemblyName = null;
var name = namesByType.GetOrDefault(serializedType);
var name = TypeNameRegistry.GetName(serializedType);
if (name != null)
{

75
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/TypeNameRegistry.cs

@ -0,0 +1,75 @@
// ==========================================================================
// TypeNameRegistry.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Reflection;
namespace PinkParrot.Infrastructure
{
public class TypeNameRegistry
{
private static readonly Dictionary<Type, string> namesByType = new Dictionary<Type, string>();
private static readonly Dictionary<string, Type> typesByName = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public static void Map(Type type, string name)
{
Guard.NotNull(type, nameof(type));
Guard.NotNull(name, nameof(name));
lock (namesByType)
{
namesByType.Add(type, name);
typesByName.Add(name, type);
}
}
public static void Map(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var typeNameAttribute = type.GetTypeInfo().GetCustomAttribute<TypeNameAttribute>();
if (!string.IsNullOrWhiteSpace(typeNameAttribute?.TypeName))
{
Map(type, typeNameAttribute.TypeName);
}
}
}
public static string GetName<T>()
{
return GetName(typeof(T));
}
public static string GetName(Type type)
{
var result = namesByType.GetOrDefault(type);
if (result == null)
{
throw new ArgumentException($"There is not name for type '{type}");
}
return result;
}
public static Type GetType(string name)
{
var result = typesByName.GetOrDefault(name);
if (result == null)
{
throw new ArgumentException($"There is not type for name '{name}");
}
return result;
}
}
}

27
src/pinkparrot_read/PinkParrot.Read/Models/ModelFieldDto.cs

@ -0,0 +1,27 @@
// ==========================================================================
// ModelFieldDto.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using Newtonsoft.Json;
using PinkParrot.Core.Schema;
namespace PinkParrot.Read.Models
{
public class ModelFieldDto
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public IModelFieldProperties Properties { get; set; }
public static ModelFieldDto Create(ModelField field)
{
return new ModelFieldDto { Name = field.Name, Properties = field.RawProperties };
}
}
}

63
src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaDto.cs

@ -0,0 +1,63 @@
// ==========================================================================
// ModelSchemaDto.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure;
using Newtonsoft.Json;
// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InvertIf
namespace PinkParrot.Read.Models
{
public sealed class ModelSchemaDto
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public Dictionary<long, ModelFieldDto> Fields { get; set; }
[JsonProperty]
public ModelSchemaProperties Properties { get; set; }
public static ModelSchemaDto Create(ModelSchema schema)
{
Guard.NotNull(schema, nameof(schema));
var dto = new ModelSchemaDto { Properties = schema.Properties, Name = schema.Name };
dto.Fields =
schema.Fields.ToDictionary(
kvp => kvp.Key,
kvp => ModelFieldDto.Create(kvp.Value));
return dto;
}
public ModelSchema ToSchema(ModelFieldRegistry registry)
{
Guard.NotNull(registry, nameof(registry));
var schema = ModelSchema.Create(Name, Properties);
if (Fields != null)
{
foreach (var kvp in Fields)
{
var field = kvp.Value;
schema = schema.AddOrUpdateField(registry.CreateField(kvp.Key, field.Name, field.Properties));
}
}
return schema;
}
}
}

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

@ -36,14 +36,14 @@ namespace PinkParrot.Read.Repositories.Implementations
public static BsonDocument ToJsonBsonDocument<T>(this T value, JsonSerializerSettings settings)
{
var json = JsonConvert.SerializeObject(value, settings).Replace("$type", "§type");
var json = JsonConvert.SerializeObject(value, settings);
return BsonDocument.Parse(json);
}
public static T ToJsonObject<T>(this BsonDocument document, JsonSerializerSettings settings)
{
var json = document.ToJson().Replace("§type", "$type");
var json = document.ToJson();
return JsonConvert.DeserializeObject<T>(json, settings);
}

2
src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaEntity.cs → src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoModelSchemaEntity.cs

@ -10,7 +10,7 @@ using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace PinkParrot.Read.Repositories.Implementations
namespace PinkParrot.Read.Repositories.Implementations.Mongo
{
public sealed class MongoModelSchemaEntity : IModelSchemaEntity
{

47
src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaRepository.cs → src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoModelSchemaRepository.cs

@ -13,30 +13,29 @@ using System.Threading.Tasks;
using MongoDB.Driver;
using Newtonsoft.Json;
using PinkParrot.Core.Schema;
using PinkParrot.Core.Schema.Json;
using PinkParrot.Events.Schema;
using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS;
using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Infrastructure.MongoDb;
using PinkParrot.Read.Models;
namespace PinkParrot.Read.Repositories.Implementations
namespace PinkParrot.Read.Repositories.Implementations.Mongo
{
public sealed class MongoModelSchemaRepository : MongoRepositoryBase<MongoModelSchemaEntity>, IModelSchemaRepository, ICatchEventConsumer
{
private readonly JsonSerializerSettings serializerSettings;
private readonly ModelFieldFactory factory;
private readonly ModelFieldRegistry fieldRegistry;
public MongoModelSchemaRepository(IMongoDatabase database, JsonSerializerSettings serializerSettings, ModelFieldFactory factory)
public MongoModelSchemaRepository(IMongoDatabase database, JsonSerializerSettings serializerSettings, ModelFieldRegistry fieldRegistry)
: base(database)
{
Guard.NotNull(serializerSettings, nameof(serializerSettings));
Guard.NotNull(factory, nameof(factory));
Guard.NotNull(fieldRegistry, nameof(fieldRegistry));
this.serializerSettings = serializerSettings;
this.factory = factory;
this.fieldRegistry = fieldRegistry;
}
protected override Task SetupCollectionAsync(IMongoCollection<MongoModelSchemaEntity> collection)
@ -83,11 +82,6 @@ namespace PinkParrot.Read.Repositories.Implementations
return Collection.UpdateAsync(headers, e => e.IsDeleted = true);
}
public Task On(ModelFieldAdded @event, EnvelopeHeaders headers)
{
return UpdateSchema(headers, s => s.AddField(@event.FieldId, @event.Properties, factory));
}
public Task On(ModelFieldDeleted @event, EnvelopeHeaders headers)
{
return UpdateSchema(headers, s => s.DeleteField(@event.FieldId));
@ -115,29 +109,28 @@ namespace PinkParrot.Read.Repositories.Implementations
public Task On(ModelFieldUpdated @event, EnvelopeHeaders headers)
{
return UpdateSchema(headers, s => s.SetField(@event.FieldId, @event.Properties));
return UpdateSchema(headers, s => s.UpdateField(@event.FieldId, @event.Properties));
}
public Task On(ModelSchemaUpdated @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(headers, e =>
{
if (!string.IsNullOrWhiteSpace(@event.Properties.Name))
{
e.Name = @event.Properties.Name;
}
return UpdateSchema(headers, s => s.Update(@event.Properties));
}
UpdateSchema(e, s => s.Update(@event.Properties));
});
public Task On(ModelFieldAdded @event, EnvelopeHeaders headers)
{
var field = fieldRegistry.CreateField(@event.FieldId, @event.Name, @event.Properties);
return UpdateSchema(headers, s => s.AddOrUpdateField(field));
}
public Task On(ModelSchemaCreated @event, EnvelopeHeaders headers)
{
return Collection.CreateAsync(headers, e =>
{
e.Name = @event.Properties.Name;
e.Name = @event.Name;
Serialize(e, ModelSchema.Create(@event.Properties));
Serialize(e, ModelSchema.Create(@event.Name, @event.Properties));
});
}
@ -162,12 +155,16 @@ namespace PinkParrot.Read.Repositories.Implementations
private void Serialize(MongoModelSchemaEntity entity, ModelSchema schema)
{
entity.Schema = SchemaDto.Create(schema).ToJsonBsonDocument(serializerSettings);
var dto = ModelSchemaDto.Create(schema);
entity.Schema = dto.ToJsonBsonDocument(serializerSettings);
}
private ModelSchema Deserialize(MongoModelSchemaEntity entity)
{
return entity?.Schema.ToJsonObject<SchemaDto>(serializerSettings).ToModelSchema(factory);
var dto = entity?.Schema.ToJsonObject<ModelSchemaDto>(serializerSettings);
return dto?.ToSchema(fieldRegistry);
}
}
}

2
src/pinkparrot_read/PinkParrot.Read/Services/Implementations/MongoPositions.cs → src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoPositions.cs

@ -10,7 +10,7 @@ using System.Runtime.Serialization;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace PinkParrot.Read.Services.Implementations
namespace PinkParrot.Read.Repositories.Implementations.Mongo
{
[DataContract]
public class MongoPosition

2
src/pinkparrot_read/PinkParrot.Read/Services/Implementations/MongoStreamPositionsStorage.cs → src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/Mongo/MongoStreamPositionsStorage.cs

@ -13,7 +13,7 @@ using PinkParrot.Infrastructure.MongoDb;
// ReSharper disable InvertIf
namespace PinkParrot.Read.Services.Implementations
namespace PinkParrot.Read.Repositories.Implementations.Mongo
{
public sealed class MongoStreamPositionsStorage : MongoRepositoryBase<MongoPosition>, IStreamPositionStorage
{

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

@ -6,12 +6,16 @@
// All rights reserved.
// ==========================================================================
using PinkParrot.Core.Schema;
using Newtonsoft.Json.Linq;
namespace PinkParrot.Write.Schema.Commands
{
public class AddModelField : TenantCommand
{
public ModelFieldProperties Properties { get; set; }
public string Name;
public string Type;
public JToken Properties;
}
}

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

@ -12,6 +12,8 @@ namespace PinkParrot.Write.Schema.Commands
{
public class CreateModelSchema : TenantCommand
{
public string Name { get; set; }
public ModelSchemaProperties Properties { get; set; }
}
}

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

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

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

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

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

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

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

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

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

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

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

@ -6,14 +6,14 @@
// All rights reserved.
// ==========================================================================
using PinkParrot.Core.Schema;
using Newtonsoft.Json.Linq;
namespace PinkParrot.Write.Schema.Commands
{
public class UpdateModelField : TenantCommand
{
public long FieldId { get; set; }
public long FieldId;
public ModelFieldProperties Properties { get; set; }
public JToken Properties { get; set; }
}
}

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

@ -8,6 +8,9 @@
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Write.Schema.Commands;
@ -16,66 +19,91 @@ namespace PinkParrot.Write.Schema
{
public class ModelSchemaCommandHandler : ICommandHandler
{
private readonly ModelFieldRegistry registry;
private readonly JsonSerializer serializer;
public ModelSchemaCommandHandler(ModelFieldRegistry registry, JsonSerializer serializer)
{
this.registry = registry;
this.serializer = serializer;
}
public Task<bool> HandleAsync(CommandContext context)
{
return context.IsHandled ? Task.FromResult(false) : this.DispatchActionAsync(context.Command, context);
}
public Task On(AddModelField command, CommandContext context)
public Task On(CreateModelSchema command, CommandContext context)
{
return Update(command, context, schema => schema.AddField(command));
var schema = context.Factory.CreateNew<ModelSchemaDomainObject>(command.AggregateId);
schema.Create(command.TenantId, command.Name, command.Properties);
return context.Repository.SaveAsync(schema, command.AggregateId);
}
public Task On(DeleteModelField command, CommandContext context)
public Task On(DeleteModelSchema command, CommandContext context)
{
return Update(command, context, schema => schema.DeleteField(command));
return UpdateAsync(command, context, s => s.Delete());
}
public Task On(DeleteModelSchema command, CommandContext context)
public Task On(DeleteModelField command, CommandContext context)
{
return Update(command, context, schema => schema.Delete(command));
return UpdateAsync(command, context, s => s.DeleteField(command.FieldId));
}
public Task On(DisableModelField command, CommandContext context)
{
return Update(command, context, schema => schema.DisableField(command));
return UpdateAsync(command, context, s => s.DisableField(command.FieldId));
}
public Task On(EnableModelField command, CommandContext context)
{
return Update(command, context, schema => schema.EnableField(command));
return UpdateAsync(command, context, s => s.EnableField(command.FieldId));
}
public Task On(HideModelField command, CommandContext context)
{
return Update(command, context, schema => schema.HideField(command));
return UpdateAsync(command, context, s => s.HideField(command.FieldId));
}
public Task On(ShowModelField command, CommandContext context)
{
return Update(command, context, schema => schema.ShowField(command));
return UpdateAsync(command, context, s => s.ShowField(command.FieldId));
}
public Task On(UpdateModelField command, CommandContext context)
public Task On(UpdateModelSchema command, CommandContext context)
{
return Update(command, context, schema => schema.UpdateField(command));
return UpdateAsync(command, context, s => s.Update(command.Properties));
}
public Task On(UpdateModelSchema command, CommandContext context)
public Task On(AddModelField command, CommandContext context)
{
return Update(command, context, schema => schema.Update(command));
var propertiesType = registry.FindByTypeName(command.Type).PropertiesType;
var propertiesValue = (IModelFieldProperties)command.Properties.ToObject(propertiesType, serializer);
return UpdateAsync(command, context, s => s.AddField(command.Name, propertiesValue));
}
public Task On(CreateModelSchema command, CommandContext context)
public Task On(UpdateModelField command, CommandContext context)
{
var schema = context.Factory.CreateNew<ModelSchemaDomainObject>(command.AggregateId);
return UpdateAsync(command, context, s =>
{
var field = s.Schema.Fields.GetOrDefault(command.FieldId);
schema.Create(command);
if (field == null)
{
throw new DomainObjectNotFoundException(command.FieldId.ToString(), typeof(ModelField));
}
return context.Repository.SaveAsync(schema, command.AggregateId);
var propertiesType = registry.FindByPropertiesType(field.RawProperties.GetType()).PropertiesType;
var propertiesValue = (IModelFieldProperties)command.Properties.ToObject(propertiesType, serializer);
s.UpdateField(command.FieldId, propertiesValue);
});
}
private static async Task Update(IAggregateCommand command, CommandContext context, Action<ModelSchemaDomainObject> updater)
private static async Task UpdateAsync(IAggregateCommand command, CommandContext context, Action<ModelSchemaDomainObject> updater)
{
var schema = await context.Repository.GetByIdAsync<ModelSchemaDomainObject>(command.AggregateId);

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

@ -13,13 +13,12 @@ using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS;
using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Write.Schema.Commands;
namespace PinkParrot.Write.Schema
{
public class ModelSchemaDomainObject : DomainObject
public class ModelSchemaDomainObject : DomainObject, ITenantAggregate
{
private readonly ModelFieldFactory factory;
private readonly ModelFieldRegistry registry;
private Guid tenantId;
private bool isDeleted;
private long totalFields;
@ -40,15 +39,17 @@ namespace PinkParrot.Write.Schema
get { return isDeleted; }
}
public ModelSchemaDomainObject(Guid id, int version, ModelFieldFactory factory)
public ModelSchemaDomainObject(Guid id, int version, ModelFieldRegistry registry)
: base(id, version)
{
this.factory = factory;
Guard.NotNull(registry, nameof(registry));
this.registry = registry;
}
public void On(ModelFieldAdded @event)
{
schema = schema.AddField(@event.FieldId, @event.Properties, factory);
schema = schema.AddOrUpdateField(registry.CreateField(@event.FieldId, @event.Name, @event.Properties));
totalFields++;
}
@ -57,12 +58,12 @@ namespace PinkParrot.Write.Schema
{
tenantId = @event.TenantId;
schema = ModelSchema.Create(@event.Properties);
schema = ModelSchema.Create(@event.Name, @event.Properties);
}
public void On(ModelFieldUpdated @event)
{
schema = schema.SetField(@event.FieldId, @event.Properties);
schema = schema.UpdateField(@event.FieldId, @event.Properties);
}
public void On(ModelFieldHidden @event)
@ -100,98 +101,98 @@ namespace PinkParrot.Write.Schema
isDeleted = false;
}
public void AddField(AddModelField command)
public void AddField(string name, IModelFieldProperties properties)
{
VerifyCreatedAndNotDeleted();
var id = ++totalFields;
schema = schema.AddField(id, command.Properties, factory);
schema = schema.AddOrUpdateField(registry.CreateField(id, name, properties));
RaiseEvent(new ModelFieldAdded { FieldId = id, Properties = command.Properties }, true);
RaiseEvent(new ModelFieldAdded { FieldId = id, Properties = properties }, true);
}
public void Create(CreateModelSchema command)
public void Create(Guid newTenantId, string name, ModelSchemaProperties properties)
{
VerifyNotCreated();
tenantId = command.TenantId;
tenantId = newTenantId;
schema = ModelSchema.Create(command.Properties);
schema = ModelSchema.Create(name, properties);
RaiseEvent(new ModelSchemaCreated { Properties = command.Properties }, true);
RaiseEvent(new ModelSchemaCreated { Properties = properties }, true);
}
public void Update(UpdateModelSchema command)
public void Update(ModelSchemaProperties properties)
{
VerifyCreatedAndNotDeleted();
schema = schema.Update(command.Properties);
schema = schema.Update(properties);
RaiseEvent(new ModelSchemaUpdated { Properties = command.Properties }, true);
RaiseEvent(new ModelSchemaUpdated { Properties = properties }, true);
}
public void UpdateField(UpdateModelField command)
public void UpdateField(long fieldId, IModelFieldProperties properties)
{
VerifyCreatedAndNotDeleted();
schema = schema.SetField(command.FieldId, command.Properties);
schema = schema.UpdateField(fieldId, properties);
RaiseEvent(new ModelFieldUpdated { FieldId = command.FieldId, Properties = command.Properties }, true);
RaiseEvent(new ModelFieldUpdated { FieldId = fieldId, Properties = properties }, true);
}
public void HideField(HideModelField command)
public void HideField(long fieldId)
{
VerifyCreatedAndNotDeleted();
schema = schema.HideField(command.FieldId);
schema = schema.HideField(fieldId);
RaiseEvent(new ModelFieldHidden { FieldId = command.FieldId }, true);
RaiseEvent(new ModelFieldHidden { FieldId = fieldId }, true);
}
public void ShowField(ShowModelField command)
public void ShowField(long fieldId)
{
VerifyCreatedAndNotDeleted();
schema = schema.ShowField(command.FieldId);
schema = schema.ShowField(fieldId);
RaiseEvent(new ModelFieldShown { FieldId = command.FieldId }, true);
RaiseEvent(new ModelFieldShown { FieldId = fieldId }, true);
}
public void DisableField(DisableModelField command)
public void DisableField(long fieldId)
{
VerifyCreatedAndNotDeleted();
schema = schema.DisableField(command.FieldId);
schema = schema.DisableField(fieldId);
RaiseEvent(new ModelFieldDisabled { FieldId = command.FieldId }, true);
RaiseEvent(new ModelFieldDisabled { FieldId = fieldId }, true);
}
public void EnableField(EnableModelField command)
public void EnableField(long fieldId)
{
VerifyCreatedAndNotDeleted();
schema = schema.EnableField(command.FieldId);
schema = schema.EnableField(fieldId);
RaiseEvent(new ModelFieldEnabled { FieldId = command.FieldId }, true);
RaiseEvent(new ModelFieldEnabled { FieldId = fieldId }, true);
}
public void Delete(DeleteModelSchema command)
public void DeleteField(long fieldId)
{
VerifyCreatedAndNotDeleted();
isDeleted = true;
schema = schema.DeleteField(fieldId);
RaiseEvent(new ModelSchemaDeleted(), true);
RaiseEvent(new ModelFieldDeleted { FieldId = fieldId }, true);
}
public void DeleteField(DeleteModelField command)
public void Delete()
{
VerifyCreatedAndNotDeleted();
schema = schema.DeleteField(command.FieldId);
isDeleted = true;
RaiseEvent(new ModelFieldDeleted { FieldId = command.FieldId }, true);
RaiseEvent(new ModelSchemaDeleted(), true);
}
private void VerifyNotCreated()

Loading…
Cancel
Save