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
|
// PinkParrot Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) PinkParrot Group
|
// 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