mirror of https://github.com/Squidex/squidex.git
52 changed files with 554 additions and 370 deletions
@ -0,0 +1,39 @@ |
|||
// ==========================================================================
|
|||
// ModelFieldProperties.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using PinkParrot.Infrastructure; |
|||
|
|||
namespace PinkParrot.Core.Schema |
|||
{ |
|||
public abstract class ModelFieldProperties : NamedElementProperties |
|||
{ |
|||
public bool IsRequired { get; } |
|||
|
|||
protected ModelFieldProperties( |
|||
bool isRequired, |
|||
string name, |
|||
string label, |
|||
string hints) |
|||
: base(name, label, hints) |
|||
{ |
|||
IsRequired = isRequired; |
|||
} |
|||
|
|||
public override void Validate(IList<ValidationError> errors) |
|||
{ |
|||
base.Validate(errors); |
|||
|
|||
ValidateCore(errors); |
|||
} |
|||
|
|||
protected virtual void ValidateCore(IList<ValidationError> errors) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// ==========================================================================
|
|||
// ModelField_Generic.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 abstract class ModelField<T> : ModelField where T : ModelFieldProperties |
|||
{ |
|||
private T properties; |
|||
|
|||
public override ModelFieldProperties RawProperties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public override string Name |
|||
{ |
|||
get { return properties.Name; } |
|||
} |
|||
|
|||
public override string Label |
|||
{ |
|||
get { return properties.Label; } |
|||
} |
|||
|
|||
public override string Hints |
|||
{ |
|||
get { return properties.Hints; } |
|||
} |
|||
|
|||
public override bool IsRequired |
|||
{ |
|||
get { return properties.IsRequired; } |
|||
} |
|||
|
|||
public T Properties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
protected ModelField(long id) |
|||
: base(id) |
|||
{ |
|||
} |
|||
|
|||
public override ModelField Configure(ModelFieldProperties newProperties, IList<ValidationError> errors) |
|||
{ |
|||
Guard.NotNull(newProperties, nameof(newProperties)); |
|||
Guard.NotNull(errors, nameof(errors)); |
|||
|
|||
var typedProperties = newProperties as T; |
|||
|
|||
if (typedProperties == null) |
|||
{ |
|||
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); |
|||
} |
|||
|
|||
newProperties.Validate(errors); |
|||
|
|||
return Update<ModelField<T>>(clone => clone.properties = typedProperties); |
|||
} |
|||
} |
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
// ==========================================================================
|
|||
// ModelSchemaMetadata.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using PinkParrot.Infrastructure; |
|||
|
|||
namespace PinkParrot.Core.Schema |
|||
{ |
|||
public sealed class ModelSchemaMetadata |
|||
{ |
|||
private string name; |
|||
private string displayName; |
|||
private string hint; |
|||
private string itemTitle; |
|||
|
|||
public string Name |
|||
{ |
|||
get { return name; } |
|||
} |
|||
|
|||
public string DisplayName |
|||
{ |
|||
get { return displayName; } |
|||
} |
|||
|
|||
public string Hint |
|||
{ |
|||
get { return hint; } |
|||
} |
|||
|
|||
public string ItemTitle |
|||
{ |
|||
get { return itemTitle; } |
|||
} |
|||
|
|||
public ModelSchemaMetadata(string name) |
|||
{ |
|||
Guard.ValidSlug(name, nameof(name)); |
|||
|
|||
this.name = name; |
|||
} |
|||
|
|||
public ModelSchemaMetadata Configure(string newName, PropertiesBag properties) |
|||
{ |
|||
Guard.NotNull(properties, nameof(properties)); |
|||
|
|||
var clone = (ModelSchemaMetadata) MemberwiseClone(); |
|||
|
|||
if (newName != null) |
|||
{ |
|||
if (!newName.IsSlug()) |
|||
{ |
|||
throw new ValidationException("Cannot update the schema.", $"'{newName}' is not a valid slug."); |
|||
} |
|||
|
|||
clone.name = newName; |
|||
} |
|||
|
|||
if (properties.Contains("Hint")) |
|||
{ |
|||
clone.hint = properties["Hint"].ToString()?.Trim(); |
|||
} |
|||
|
|||
if (properties.Contains("DisplayName")) |
|||
{ |
|||
clone.displayName = properties["DisplayName"].ToString()?.Trim(); |
|||
} |
|||
|
|||
if (properties.Contains("ItemTitle")) |
|||
{ |
|||
clone.itemTitle = properties["ItemTitle"].ToString()?.Trim(); |
|||
} |
|||
|
|||
return clone; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// ==========================================================================
|
|||
// ModelSchemaMetadata.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
namespace PinkParrot.Core.Schema |
|||
{ |
|||
public sealed class ModelSchemaProperties : NamedElementProperties |
|||
{ |
|||
public ModelSchemaProperties( |
|||
string name, |
|||
string label, |
|||
string hints) |
|||
: base(name, label, hints) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// ==========================================================================
|
|||
// NamedElementProperties.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using PinkParrot.Infrastructure; |
|||
|
|||
namespace PinkParrot.Core.Schema |
|||
{ |
|||
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 |
|||
{ |
|||
get { return string.IsNullOrWhiteSpace(label) ? name : label; } |
|||
} |
|||
|
|||
public string Hints |
|||
{ |
|||
get { return hints; } |
|||
} |
|||
|
|||
protected NamedElementProperties( |
|||
string name, |
|||
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")); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// ==========================================================================
|
|||
// NumberFieldProperties.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using PinkParrot.Infrastructure; |
|||
|
|||
namespace PinkParrot.Core.Schema |
|||
{ |
|||
[TypeName("Number")] |
|||
public sealed class NumberFieldProperties : ModelFieldProperties |
|||
{ |
|||
public double? MaxValue { get; } |
|||
|
|||
public double? MinValue { get; } |
|||
|
|||
public NumberFieldProperties( |
|||
bool isRequired, |
|||
string name, |
|||
string label, |
|||
string hints, |
|||
double? minValue, |
|||
double? maxValue) |
|||
: base(isRequired, name, label, hints) |
|||
{ |
|||
MinValue = minValue; |
|||
MaxValue = maxValue; |
|||
} |
|||
|
|||
protected override void ValidateCore(IList<ValidationError> errors) |
|||
{ |
|||
if (MaxValue.HasValue && MinValue.HasValue && MinValue.Value > MaxValue.Value) |
|||
{ |
|||
errors.Add(new ValidationError("MinValue cannot be larger than max value", "MinValue", "MaxValue")); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// ==========================================================================
|
|||
// DomainException.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace PinkParrot.Infrastructure |
|||
{ |
|||
public class DomainException : Exception |
|||
{ |
|||
public DomainException(string message) |
|||
: base(message) |
|||
{ |
|||
} |
|||
|
|||
public DomainException(string message, Exception inner) |
|||
: base(message, inner) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
// ==========================================================================
|
|||
// JsonPropertiesBagConverter.cs
|
|||
// PropertiesBagConverter.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
@ -0,0 +1,71 @@ |
|||
// ==========================================================================
|
|||
// TypeNameSerializationBinder.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
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); |
|||
|
|||
return type ?? base.BindToType(assemblyName, typeName); |
|||
} |
|||
|
|||
public override void BindToName(Type serializedType, out string assemblyName, out string typeName) |
|||
{ |
|||
assemblyName = null; |
|||
|
|||
var name = namesByType.GetOrDefault(serializedType); |
|||
|
|||
if (name != null) |
|||
{ |
|||
typeName = name; |
|||
} |
|||
else |
|||
{ |
|||
base.BindToName(serializedType, out assemblyName, out typeName); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,38 @@ |
|||
// ==========================================================================
|
|||
// ValidationError.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace PinkParrot.Infrastructure |
|||
{ |
|||
public sealed class ValidationError |
|||
{ |
|||
private static readonly string[] FalbackProperties = new string[0]; |
|||
private readonly string message; |
|||
private readonly string[] propertyNames; |
|||
|
|||
public string Message |
|||
{ |
|||
get { return message; } |
|||
} |
|||
|
|||
public IEnumerable<string> PropertyNames |
|||
{ |
|||
get { return propertyNames; } |
|||
} |
|||
|
|||
public ValidationError(string message, params string[] propertyNames) |
|||
{ |
|||
Guard.NotNullOrEmpty(message, nameof(message)); |
|||
|
|||
this.message = message; |
|||
|
|||
this.propertyNames = propertyNames ?? FalbackProperties; |
|||
} |
|||
} |
|||
} |
|||
@ -1,70 +0,0 @@ |
|||
// ==========================================================================
|
|||
// ModelSchemaNameMap.cs
|
|||
// PinkParrot Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) PinkParrot Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using PinkParrot.Events.Schema; |
|||
using PinkParrot.Infrastructure.CQRS; |
|||
using PinkParrot.Infrastructure.CQRS.Events; |
|||
using PinkParrot.Infrastructure.Dispatching; |
|||
// ReSharper disable InvertIf
|
|||
|
|||
namespace PinkParrot.Write.Schema |
|||
{ |
|||
public class ModelSchemaNameMap : DomainObject |
|||
{ |
|||
private readonly Dictionary<string, Guid> schemaIdsByName = new Dictionary<string, Guid>(); |
|||
private readonly Dictionary<Guid, string> schemaNamesByIds = new Dictionary<Guid, string>(); |
|||
|
|||
public ModelSchemaNameMap(Guid id, int version) |
|||
: base(id, version) |
|||
{ |
|||
} |
|||
|
|||
protected void On(ModelSchemaDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
var aggregateId = headers.AggregateId(); |
|||
|
|||
string oldName; |
|||
|
|||
if (schemaNamesByIds.TryGetValue(aggregateId, out oldName)) |
|||
{ |
|||
schemaIdsByName.Remove(oldName); |
|||
schemaNamesByIds.Remove(aggregateId); |
|||
} |
|||
} |
|||
|
|||
protected void On(ModelSchemaUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
var aggregateId = headers.AggregateId(); |
|||
|
|||
string oldName; |
|||
|
|||
if (schemaNamesByIds.TryGetValue(aggregateId, out oldName)) |
|||
{ |
|||
schemaIdsByName.Remove(oldName); |
|||
schemaIdsByName[@event.NewName] = aggregateId; |
|||
} |
|||
} |
|||
|
|||
protected void On(ModelSchemaCreated @event, EnvelopeHeaders headers) |
|||
{ |
|||
schemaIdsByName[@event.Name] = headers.AggregateId(); |
|||
} |
|||
|
|||
public void Apply(Envelope<IEvent> @event) |
|||
{ |
|||
ApplyEvent(@event); |
|||
} |
|||
|
|||
protected override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
this.DispatchAction(@event.Payload, @event.Headers); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue