mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
410 changed files with 9874 additions and 6531 deletions
@ -0,0 +1,77 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.Contracts; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class ArrayField : RootField<ArrayFieldProperties>, IArrayField |
|||
{ |
|||
private FieldCollection<NestedField> fields = FieldCollection<NestedField>.Empty; |
|||
|
|||
public IReadOnlyList<NestedField> Fields |
|||
{ |
|||
get { return fields.Ordered; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<long, NestedField> FieldsById |
|||
{ |
|||
get { return fields.ById; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<string, NestedField> FieldsByName |
|||
{ |
|||
get { return fields.ByName; } |
|||
} |
|||
|
|||
public ArrayField(long id, string name, Partitioning partitioning, ArrayFieldProperties properties) |
|||
: base(id, name, partitioning, properties) |
|||
{ |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField DeleteField(long fieldId) |
|||
{ |
|||
return Updatefields(f => f.Remove(fieldId)); |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField ReorderFields(List<long> ids) |
|||
{ |
|||
return Updatefields(f => f.Reorder(ids)); |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField AddField(NestedField field) |
|||
{ |
|||
return Updatefields(f => f.Add(field)); |
|||
} |
|||
|
|||
[Pure] |
|||
public ArrayField UpdateField(long fieldId, Func<NestedField, NestedField> updater) |
|||
{ |
|||
return Updatefields(f => f.Update(fieldId, updater)); |
|||
} |
|||
|
|||
private ArrayField Updatefields(Func<FieldCollection<NestedField>, FieldCollection<NestedField>> updater) |
|||
{ |
|||
var newFields = updater(fields); |
|||
|
|||
if (ReferenceEquals(newFields, fields)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
return Clone<ArrayField>(clone => |
|||
{ |
|||
clone.fields = newFields; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
[TypeName("ArrayField")] |
|||
public sealed class ArrayFieldProperties : FieldProperties |
|||
{ |
|||
public int? MinItems { get; set; } |
|||
|
|||
public int? MaxItems { get; set; } |
|||
|
|||
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor) |
|||
{ |
|||
return visitor.Visit(this); |
|||
} |
|||
|
|||
public override T Accept<T>(IFieldVisitor<T> visitor, IField field) |
|||
{ |
|||
return visitor.Visit((IArrayField)field); |
|||
} |
|||
|
|||
public override RootField CreateRootField(long id, string name, Partitioning partitioning) |
|||
{ |
|||
return Fields.Array(id, name, partitioning, this); |
|||
} |
|||
|
|||
public override NestedField CreateNestedField(long id, string name) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Diagnostics.Contracts; |
|||
using System.Linq; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public sealed class FieldCollection<T> : Cloneable<FieldCollection<T>> where T : IField |
|||
{ |
|||
public static readonly FieldCollection<T> Empty = new FieldCollection<T>(); |
|||
|
|||
private ImmutableArray<T> fieldsOrdered = ImmutableArray<T>.Empty; |
|||
private ImmutableDictionary<long, T> fieldsById; |
|||
private ImmutableDictionary<string, T> fieldsByName; |
|||
|
|||
public IReadOnlyList<T> Ordered |
|||
{ |
|||
get { return fieldsOrdered; } |
|||
} |
|||
|
|||
public IReadOnlyDictionary<long, T> ById |
|||
{ |
|||
get |
|||
{ |
|||
if (fieldsById == null) |
|||
{ |
|||
if (fieldsOrdered.Length == 0) |
|||
{ |
|||
fieldsById = ImmutableDictionary<long, T>.Empty; |
|||
} |
|||
else |
|||
{ |
|||
fieldsById = fieldsOrdered.ToImmutableDictionary(x => x.Id); |
|||
} |
|||
} |
|||
|
|||
return fieldsById; |
|||
} |
|||
} |
|||
|
|||
public IReadOnlyDictionary<string, T> ByName |
|||
{ |
|||
get |
|||
{ |
|||
if (fieldsByName == null) |
|||
{ |
|||
if (fieldsOrdered.Length == 0) |
|||
{ |
|||
fieldsByName = ImmutableDictionary<string, T>.Empty; |
|||
} |
|||
else |
|||
{ |
|||
fieldsByName = fieldsOrdered.ToImmutableDictionary(x => x.Name); |
|||
} |
|||
} |
|||
|
|||
return fieldsByName; |
|||
} |
|||
} |
|||
|
|||
private FieldCollection() |
|||
{ |
|||
} |
|||
|
|||
public FieldCollection(T[] fields) |
|||
{ |
|||
Guard.NotNull(fields, nameof(fields)); |
|||
|
|||
fieldsOrdered = ImmutableArray.Create(fields); |
|||
} |
|||
|
|||
protected override void OnCloned() |
|||
{ |
|||
fieldsById = null; |
|||
fieldsByName = null; |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Remove(long fieldId) |
|||
{ |
|||
if (!ById.TryGetValue(fieldId, out var field)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = fieldsOrdered.Remove(field); |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Reorder(List<long> ids) |
|||
{ |
|||
Guard.NotNull(ids, nameof(ids)); |
|||
|
|||
if (ids.Count != fieldsOrdered.Length || ids.Any(x => !ById.ContainsKey(x))) |
|||
{ |
|||
throw new ArgumentException("Ids must cover all fields.", nameof(ids)); |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id)).ToImmutableArray(); |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Add(T field) |
|||
{ |
|||
Guard.NotNull(field, nameof(field)); |
|||
|
|||
if (ByName.ContainsKey(field.Name) || ById.ContainsKey(field.Id)) |
|||
{ |
|||
throw new ArgumentException($"A field with name '{field.Name}' and id {field.Id} already exists.", nameof(field)); |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = clone.fieldsOrdered.Add(field); |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public FieldCollection<T> Update(long fieldId, Func<T, T> updater) |
|||
{ |
|||
Guard.NotNull(updater, nameof(updater)); |
|||
|
|||
if (!ById.TryGetValue(fieldId, out var field)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
var newField = updater(field); |
|||
|
|||
if (ReferenceEquals(newField, field)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (!(newField is T typedField)) |
|||
{ |
|||
throw new InvalidOperationException($"Field must be of type {typeof(T)}"); |
|||
} |
|||
|
|||
return Clone(clone => |
|||
{ |
|||
clone.fieldsOrdered = clone.fieldsOrdered.Replace(field, typedField); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public static class FieldExtensions |
|||
{ |
|||
public static Schema ReorderFields(this Schema schema, List<long> ids, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.ReorderFields(ids); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.ReorderFields(ids); |
|||
} |
|||
|
|||
public static Schema DeleteField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.DeleteField(fieldId); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.DeleteField(fieldId); |
|||
} |
|||
|
|||
public static Schema LockField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Lock()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Lock()); |
|||
} |
|||
|
|||
public static Schema HideField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Hide()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Hide()); |
|||
} |
|||
|
|||
public static Schema ShowField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Show()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Show()); |
|||
} |
|||
|
|||
public static Schema EnableField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Enable()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Enable()); |
|||
} |
|||
|
|||
public static Schema DisableField(this Schema schema, long fieldId, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Disable()); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Disable()); |
|||
} |
|||
|
|||
public static Schema UpdateField(this Schema schema, long fieldId, FieldProperties properties, long? parentId = null) |
|||
{ |
|||
if (parentId != null) |
|||
{ |
|||
return schema.UpdateField(parentId.Value, f => |
|||
{ |
|||
if (f is ArrayField arrayField) |
|||
{ |
|||
return arrayField.UpdateField(fieldId, n => n.Update(properties)); |
|||
} |
|||
|
|||
return f; |
|||
}); |
|||
} |
|||
|
|||
return schema.UpdateField(fieldId, f => f.Update(properties)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface IArrayField : IField<ArrayFieldProperties> |
|||
{ |
|||
IReadOnlyList<NestedField> Fields { get; } |
|||
|
|||
IReadOnlyDictionary<long, NestedField> FieldsById { get; } |
|||
|
|||
IReadOnlyDictionary<string, NestedField> FieldsByName { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface INestedField : IField |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public interface IRootField : IField |
|||
{ |
|||
Partitioning Partitioning { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas.Json |
|||
{ |
|||
public sealed class JsonNestedFieldModel |
|||
{ |
|||
[JsonProperty] |
|||
public long Id { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public bool IsHidden { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public bool IsDisabled { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public FieldProperties Properties { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public abstract class NestedField : Cloneable<NestedField>, INestedField |
|||
{ |
|||
private readonly long fieldId; |
|||
private readonly string fieldName; |
|||
private bool isDisabled; |
|||
private bool isHidden; |
|||
private bool isLocked; |
|||
|
|||
public long Id |
|||
{ |
|||
get { return fieldId; } |
|||
} |
|||
|
|||
public string Name |
|||
{ |
|||
get { return fieldName; } |
|||
} |
|||
|
|||
public bool IsLocked |
|||
{ |
|||
get { return isLocked; } |
|||
} |
|||
|
|||
public bool IsHidden |
|||
{ |
|||
get { return isHidden; } |
|||
} |
|||
|
|||
public bool IsDisabled |
|||
{ |
|||
get { return isDisabled; } |
|||
} |
|||
|
|||
public abstract FieldProperties RawProperties { get; } |
|||
|
|||
protected NestedField(long id, string name) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
Guard.GreaterThan(id, 0, nameof(id)); |
|||
|
|||
fieldId = id; |
|||
fieldName = name; |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Lock() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isLocked = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Hide() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Show() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isHidden = false; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Disable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = true; |
|||
}); |
|||
} |
|||
|
|||
[Pure] |
|||
public NestedField Enable() |
|||
{ |
|||
return Clone(clone => |
|||
{ |
|||
clone.isDisabled = false; |
|||
}); |
|||
} |
|||
|
|||
public abstract T Accept<T>(IFieldVisitor<T> visitor); |
|||
|
|||
public abstract NestedField Update(FieldProperties newProperties); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public class NestedField<T> : NestedField, IField<T> where T : FieldProperties, new() |
|||
{ |
|||
private T properties; |
|||
|
|||
public T Properties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public override FieldProperties RawProperties |
|||
{ |
|||
get { return properties; } |
|||
} |
|||
|
|||
public NestedField(long id, string name, T properties) |
|||
: base(id, name) |
|||
{ |
|||
Guard.NotNull(properties, nameof(properties)); |
|||
|
|||
SetProperties(properties); |
|||
} |
|||
|
|||
[Pure] |
|||
public override NestedField Update(FieldProperties newProperties) |
|||
{ |
|||
var typedProperties = ValidateProperties(newProperties); |
|||
|
|||
return Clone<NestedField<T>>(clone => |
|||
{ |
|||
clone.SetProperties(typedProperties); |
|||
}); |
|||
} |
|||
|
|||
private void SetProperties(T newProperties) |
|||
{ |
|||
properties = newProperties; |
|||
properties.Freeze(); |
|||
} |
|||
|
|||
private T ValidateProperties(FieldProperties newProperties) |
|||
{ |
|||
Guard.NotNull(newProperties, nameof(newProperties)); |
|||
|
|||
if (!(newProperties is T typedProperties)) |
|||
{ |
|||
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); |
|||
} |
|||
|
|||
return typedProperties; |
|||
} |
|||
|
|||
public override TResult Accept<TResult>(IFieldVisitor<TResult> visitor) |
|||
{ |
|||
return properties.Accept(visitor, this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ConvertContent |
|||
{ |
|||
public static class Value |
|||
{ |
|||
public static readonly JToken Unset = JValue.CreateUndefined(); |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Text; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ConvertContent |
|||
{ |
|||
public delegate JToken ValueConverter(JToken value, IField field); |
|||
|
|||
public static class ValueConverters |
|||
{ |
|||
public static ValueConverter DecodeJson() |
|||
{ |
|||
return (value, field) => |
|||
{ |
|||
if (!value.IsNull() && field is IField<JsonFieldProperties>) |
|||
{ |
|||
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(value.ToString())); |
|||
|
|||
return JToken.Parse(decoded); |
|||
} |
|||
|
|||
return value; |
|||
}; |
|||
} |
|||
|
|||
public static ValueConverter EncodeJson() |
|||
{ |
|||
return (value, field) => |
|||
{ |
|||
if (!value.IsNull() && field is IField<JsonFieldProperties>) |
|||
{ |
|||
var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(value.ToString())); |
|||
|
|||
return encoded; |
|||
} |
|||
|
|||
return value; |
|||
}; |
|||
} |
|||
|
|||
public static ValueConverter ExcludeHidden() |
|||
{ |
|||
return (value, field) => |
|||
{ |
|||
return field.IsHidden ? Value.Unset : value; |
|||
}; |
|||
} |
|||
|
|||
public static ValueConverter ExcludeChangedTypes() |
|||
{ |
|||
return (value, field) => |
|||
{ |
|||
if (value.IsNull()) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
JsonValueConverter.ConvertValue(field, value); |
|||
} |
|||
catch |
|||
{ |
|||
return Value.Unset; |
|||
} |
|||
|
|||
return value; |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds |
|||
{ |
|||
public static class ReferencesExtensions |
|||
{ |
|||
public static IEnumerable<Guid> ExtractReferences(this IField field, JToken value) |
|||
{ |
|||
return ReferencesExtractor.ExtractReferences(field, value); |
|||
} |
|||
|
|||
public static JToken CleanReferences(this IField field, JToken value, ICollection<Guid> oldReferences) |
|||
{ |
|||
if (value.IsNull()) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
return ReferencesCleaner.CleanReferences(field, value, oldReferences); |
|||
} |
|||
|
|||
public static JToken ToJToken(this HashSet<Guid> ids) |
|||
{ |
|||
var result = new JArray(); |
|||
|
|||
foreach (var id in ids) |
|||
{ |
|||
result.Add(new JValue(id)); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static HashSet<Guid> ToGuidSet(this JToken value) |
|||
{ |
|||
if (value is JArray ids) |
|||
{ |
|||
var result = new HashSet<Guid>(); |
|||
|
|||
foreach (var id in ids) |
|||
{ |
|||
if (id.Type == JTokenType.Guid) |
|||
{ |
|||
result.Add((Guid)id); |
|||
} |
|||
else if (id.Type == JTokenType.String && Guid.TryParse((string)id, out var guid)) |
|||
{ |
|||
result.Add(guid); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return new HashSet<Guid>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,57 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent |
|||
{ |
|||
public static class FieldExtensions |
|||
{ |
|||
public static void AddError(this ConcurrentBag<ValidationError> errors, string message, IField field, IFieldPartitionItem partitionItem = null) |
|||
{ |
|||
AddError(errors, message, !string.IsNullOrWhiteSpace(field.RawProperties.Label) ? field.RawProperties.Label : field.Name, field.Name, partitionItem); |
|||
} |
|||
|
|||
public static void AddError(this ConcurrentBag<ValidationError> errors, string message, string fieldName, IFieldPartitionItem partitionItem = null) |
|||
{ |
|||
AddError(errors, message, fieldName, fieldName, partitionItem); |
|||
} |
|||
|
|||
public static void AddError(this ConcurrentBag<ValidationError> errors, string message, string displayName, string fieldName, IFieldPartitionItem partitionItem = null) |
|||
{ |
|||
if (partitionItem != null && partitionItem != InvariantPartitioning.Instance.Master) |
|||
{ |
|||
displayName += $" ({partitionItem.Key})"; |
|||
} |
|||
|
|||
errors.Add(new ValidationError(message.Replace("<FIELD>", displayName), fieldName)); |
|||
} |
|||
|
|||
public static async Task ValidateAsync(this IField field, JToken value, ValidationContext context, Action<string> addError) |
|||
{ |
|||
try |
|||
{ |
|||
var typedValue = value.IsNull() ? null : JsonValueConverter.ConvertValue(field, value); |
|||
|
|||
foreach (var validator in ValidatorsFactory.CreateValidators(field)) |
|||
{ |
|||
await validator.ValidateAsync(typedValue, context, addError); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
addError("<FIELD> is not a valid value."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent |
|||
{ |
|||
public static class ObjectPath |
|||
{ |
|||
public static string ToPathString(this IEnumerable<string> path) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
var index = 0; |
|||
foreach (var property in path) |
|||
{ |
|||
if (index == 0) |
|||
{ |
|||
sb.Append(property); |
|||
} |
|||
else if (index == 1) |
|||
{ |
|||
if (!property.Equals(InvariantPartitioning.Instance.Master.Key, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
sb.Append("("); |
|||
sb.Append(property); |
|||
sb.Append(")"); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (property[0] != '[') |
|||
{ |
|||
sb.Append("."); |
|||
} |
|||
|
|||
sb.Append(property); |
|||
} |
|||
|
|||
index++; |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators |
|||
{ |
|||
public sealed class FieldValidator : IValidator |
|||
{ |
|||
private readonly IValidator[] validators; |
|||
private readonly IField field; |
|||
|
|||
public FieldValidator(IValidator[] validators, IField field) |
|||
{ |
|||
this.validators = validators; |
|||
this.field = field; |
|||
} |
|||
|
|||
public async Task ValidateAsync(object value, ValidationContext context, AddError addError) |
|||
{ |
|||
try |
|||
{ |
|||
object typedValue = null; |
|||
|
|||
if (value is JToken jToken) |
|||
{ |
|||
typedValue = jToken.IsNull() ? null : JsonValueConverter.ConvertValue(field, jToken); |
|||
} |
|||
|
|||
var tasks = new List<Task>(); |
|||
|
|||
foreach (var validator in ValidatorsFactory.CreateValidators(field)) |
|||
{ |
|||
tasks.Add(validator.ValidateAsync(typedValue, context, addError)); |
|||
} |
|||
|
|||
await Task.WhenAll(tasks); |
|||
} |
|||
catch |
|||
{ |
|||
addError(context.Path, "Not a valid value."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators |
|||
{ |
|||
public sealed class ObjectValidator<TValue> : IValidator |
|||
{ |
|||
private readonly IDictionary<string, (bool IsOptional, IValidator Validator)> schema; |
|||
private readonly bool isPartial; |
|||
private readonly string fieldType; |
|||
private readonly TValue fieldDefault; |
|||
|
|||
public ObjectValidator(IDictionary<string, (bool IsOptional, IValidator Validator)> schema, bool isPartial, string fieldType, TValue fieldDefault) |
|||
{ |
|||
this.schema = schema; |
|||
this.fieldDefault = fieldDefault; |
|||
this.fieldType = fieldType; |
|||
this.isPartial = isPartial; |
|||
} |
|||
|
|||
public async Task ValidateAsync(object value, ValidationContext context, AddError addError) |
|||
{ |
|||
if (value is IDictionary<string, TValue> values) |
|||
{ |
|||
foreach (var fieldData in values) |
|||
{ |
|||
var name = fieldData.Key; |
|||
|
|||
if (!schema.ContainsKey(name)) |
|||
{ |
|||
addError(context.Path.Enqueue(name), $"Not a known {fieldType}."); |
|||
} |
|||
} |
|||
|
|||
var tasks = new List<Task>(); |
|||
|
|||
foreach (var field in schema) |
|||
{ |
|||
var name = field.Key; |
|||
|
|||
if (!values.TryGetValue(name, out var fieldValue)) |
|||
{ |
|||
if (isPartial) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
fieldValue = fieldDefault; |
|||
} |
|||
|
|||
var (isOptional, validator) = field.Value; |
|||
var fieldContext = context.Nested(name).Optional(isOptional); |
|||
|
|||
tasks.Add(validator.ValidateAsync(fieldValue, fieldContext, addError)); |
|||
} |
|||
|
|||
await Task.WhenAll(tasks); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,344 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using GraphQL; |
|||
using GraphQL.Resolvers; |
|||
using GraphQL.Types; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class AppMutationsGraphType : ObjectGraphType |
|||
{ |
|||
public AppMutationsGraphType(IGraphModel model, IEnumerable<ISchemaEntity> schemas) |
|||
{ |
|||
foreach (var schema in schemas) |
|||
{ |
|||
var schemaId = schema.NamedId(); |
|||
var schemaType = schema.TypeName(); |
|||
var schemaName = schema.DisplayName(); |
|||
|
|||
var contentType = model.GetContentType(schema.Id); |
|||
var contentDataType = model.GetContentDataType(schema.Id); |
|||
|
|||
var resultType = new ContentDataChangedResultGraphType(schemaType, schemaName, contentDataType); |
|||
|
|||
var inputType = new ContentDataGraphInputType(model, schema); |
|||
|
|||
AddContentCreate(schemaId, schemaType, schemaName, inputType, contentDataType, contentType); |
|||
AddContentUpdate(schemaType, schemaName, inputType, resultType); |
|||
AddContentPatch(schemaType, schemaName, inputType, resultType); |
|||
AddContentPublish(schemaType, schemaName); |
|||
AddContentUnpublish(schemaType, schemaName); |
|||
AddContentArchive(schemaType, schemaName); |
|||
AddContentRestore(schemaType, schemaName); |
|||
AddContentDelete(schemaType, schemaName); |
|||
} |
|||
|
|||
Description = "The app mutations."; |
|||
} |
|||
|
|||
private void AddContentCreate(NamedId<Guid> schemaId, string schemaType, string schemaName, ContentDataGraphInputType inputType, IGraphType contentDataType, IGraphType contentType) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"create{schemaType}Content", |
|||
Arguments = new QueryArguments |
|||
{ |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "data", |
|||
Description = $"The data for the {schemaName} content.", |
|||
DefaultValue = null, |
|||
ResolvedType = new NonNullGraphType(inputType), |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "publish", |
|||
Description = "Set to true to autopublish content.", |
|||
DefaultValue = false, |
|||
ResolvedType = AllTypes.Boolean |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "expectedVersion", |
|||
Description = "The expected version", |
|||
DefaultValue = EtagVersion.Any, |
|||
ResolvedType = AllTypes.Int |
|||
} |
|||
}, |
|||
ResolvedType = new NonNullGraphType(contentType), |
|||
Resolver = ResolveAsync(async (c, publish) => |
|||
{ |
|||
var argPublish = c.GetArgument<bool>("publish"); |
|||
|
|||
var contentData = GetContentData(c); |
|||
|
|||
var command = new CreateContent { SchemaId = schemaId, Data = contentData, Publish = argPublish }; |
|||
var commandContext = await publish(command); |
|||
|
|||
var result = commandContext.Result<EntityCreatedResult<NamedContentData>>(); |
|||
var response = ContentEntity.Create(command, result); |
|||
|
|||
return (IContentEntity)ContentEntity.Create(command, result); |
|||
}), |
|||
Description = $"Creates an {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentUpdate(string schemaType, string schemaName, ContentDataGraphInputType inputType, IGraphType resultType) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"update{schemaType}Content", |
|||
Arguments = new QueryArguments |
|||
{ |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "id", |
|||
Description = $"The id of the {schemaName} content (GUID)", |
|||
DefaultValue = string.Empty, |
|||
ResolvedType = AllTypes.NonNullGuid |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "data", |
|||
Description = $"The data for the {schemaName} content.", |
|||
DefaultValue = null, |
|||
ResolvedType = new NonNullGraphType(inputType), |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "expectedVersion", |
|||
Description = "The expected version", |
|||
DefaultValue = EtagVersion.Any, |
|||
ResolvedType = AllTypes.Int |
|||
} |
|||
}, |
|||
ResolvedType = new NonNullGraphType(resultType), |
|||
Resolver = ResolveAsync(async (c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
var contentData = GetContentData(c); |
|||
|
|||
var command = new UpdateContent { ContentId = contentId, Data = contentData }; |
|||
var commandContext = await publish(command); |
|||
|
|||
var result = commandContext.Result<ContentDataChangedResult>(); |
|||
|
|||
return result; |
|||
}), |
|||
Description = $"Update an {schemaName} content by id." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentPatch(string schemaType, string schemaName, ContentDataGraphInputType inputType, IGraphType resultType) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"patch{schemaType}Content", |
|||
Arguments = new QueryArguments |
|||
{ |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "id", |
|||
Description = $"The id of the {schemaName} content (GUID)", |
|||
DefaultValue = string.Empty, |
|||
ResolvedType = AllTypes.NonNullGuid |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "data", |
|||
Description = $"The data for the {schemaName} content.", |
|||
DefaultValue = null, |
|||
ResolvedType = new NonNullGraphType(inputType), |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "expectedVersion", |
|||
Description = "The expected version", |
|||
DefaultValue = EtagVersion.Any, |
|||
ResolvedType = AllTypes.Int |
|||
} |
|||
}, |
|||
ResolvedType = new NonNullGraphType(resultType), |
|||
Resolver = ResolveAsync(async (c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
var contentData = GetContentData(c); |
|||
|
|||
var command = new PatchContent { ContentId = contentId, Data = contentData }; |
|||
var commandContext = await publish(command); |
|||
|
|||
var result = commandContext.Result<ContentDataChangedResult>(); |
|||
|
|||
return result; |
|||
}), |
|||
Description = $"Patch a {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentPublish(string schemaType, string schemaName) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"publish{schemaType}Content", |
|||
Arguments = CreateIdArguments(schemaName), |
|||
ResolvedType = AllTypes.CommandVersion, |
|||
Resolver = ResolveAsync((c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
|
|||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Published }; |
|||
|
|||
return publish(command); |
|||
}), |
|||
Description = $"Publish a {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentUnpublish(string schemaType, string schemaName) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"unpublish{schemaType}Content", |
|||
Arguments = CreateIdArguments(schemaName), |
|||
ResolvedType = AllTypes.CommandVersion, |
|||
Resolver = ResolveAsync((c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
|
|||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Draft }; |
|||
|
|||
return publish(command); |
|||
}), |
|||
Description = $"Unpublish a {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentArchive(string schemaType, string schemaName) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"archive{schemaType}Content", |
|||
Arguments = CreateIdArguments(schemaName), |
|||
ResolvedType = AllTypes.CommandVersion, |
|||
Resolver = ResolveAsync((c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
|
|||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Archived }; |
|||
|
|||
return publish(command); |
|||
}), |
|||
Description = $"Archive a {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentRestore(string schemaType, string schemaName) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"restore{schemaType}Content", |
|||
Arguments = CreateIdArguments(schemaName), |
|||
ResolvedType = AllTypes.CommandVersion, |
|||
Resolver = ResolveAsync((c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
|
|||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Draft }; |
|||
|
|||
return publish(command); |
|||
}), |
|||
Description = $"Restore a {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private void AddContentDelete(string schemaType, string schemaName) |
|||
{ |
|||
AddField(new FieldType |
|||
{ |
|||
Name = $"delete{schemaType}Content", |
|||
Arguments = CreateIdArguments(schemaName), |
|||
ResolvedType = AllTypes.CommandVersion, |
|||
Resolver = ResolveAsync((c, publish) => |
|||
{ |
|||
var contentId = c.GetArgument<Guid>("id"); |
|||
|
|||
var command = new DeleteContent { ContentId = contentId }; |
|||
|
|||
return publish(command); |
|||
}), |
|||
Description = $"Delete an {schemaName} content." |
|||
}); |
|||
} |
|||
|
|||
private static QueryArguments CreateIdArguments(string schemaName) |
|||
{ |
|||
return new QueryArguments |
|||
{ |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "id", |
|||
Description = $"The id of the {schemaName} content (GUID)", |
|||
DefaultValue = string.Empty, |
|||
ResolvedType = AllTypes.NonNullGuid |
|||
}, |
|||
new QueryArgument(AllTypes.None) |
|||
{ |
|||
Name = "expectedVersion", |
|||
Description = "The expected version", |
|||
DefaultValue = EtagVersion.Any, |
|||
ResolvedType = AllTypes.Int |
|||
} |
|||
}; |
|||
} |
|||
|
|||
private static IFieldResolver ResolveAsync<T>(Func<ResolveFieldContext, Func<SquidexCommand, Task<CommandContext>>, Task<T>> action) |
|||
{ |
|||
return new FuncFieldResolver<Task<T>>(async c => |
|||
{ |
|||
var e = (GraphQLExecutionContext)c.UserContext; |
|||
|
|||
try |
|||
{ |
|||
return await action(c, command => |
|||
{ |
|||
command.ExpectedVersion = c.GetArgument("expectedVersion", EtagVersion.Any); |
|||
|
|||
return e.CommandBus.PublishAsync(command); |
|||
}); |
|||
} |
|||
catch (ValidationException ex) |
|||
{ |
|||
c.Errors.Add(new ExecutionError(ex.Message)); |
|||
|
|||
throw; |
|||
} |
|||
catch (DomainException ex) |
|||
{ |
|||
c.Errors.Add(new ExecutionError(ex.Message)); |
|||
|
|||
throw; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private static NamedContentData GetContentData(ResolveFieldContext c) |
|||
{ |
|||
return JObject.FromObject(c.GetArgument<object>("data")).ToObject<NamedContentData>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,44 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GraphQL.Resolvers; |
|||
using GraphQL.Types; |
|||
using Squidex.Infrastructure.Commands; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class CommandVersionGraphType : ObjectGraphType<CommandContext> |
|||
{ |
|||
public CommandVersionGraphType() |
|||
{ |
|||
Name = "CommandVersionDto"; |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = "version", |
|||
ResolvedType = AllTypes.Int, |
|||
Resolver = ResolveVersion(), |
|||
Description = "The new version of the item." |
|||
}); |
|||
|
|||
Description = "The result of a mutation"; |
|||
} |
|||
|
|||
private static IFieldResolver ResolveVersion() |
|||
{ |
|||
return new FuncFieldResolver<CommandContext, int?>(x => |
|||
{ |
|||
if (x.Source.Result<object>() is EntitySavedResult result) |
|||
{ |
|||
return (int)result.Version; |
|||
} |
|||
|
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,44 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using GraphQL.Resolvers; |
|||
using GraphQL.Types; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class ContentDataChangedResultGraphType : ObjectGraphType<ContentDataChangedResult> |
|||
{ |
|||
public ContentDataChangedResultGraphType(string schemaType, string schemaName, IGraphType contentDataType) |
|||
{ |
|||
Name = $"{schemaName}DataChangedResultDto"; |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = "version", |
|||
ResolvedType = AllTypes.Int, |
|||
Resolver = Resolve(x => x.Version), |
|||
Description = $"The new version of the {schemaName} content." |
|||
}); |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = "data", |
|||
ResolvedType = new NonNullGraphType(contentDataType), |
|||
Resolver = Resolve(x => x.Data), |
|||
Description = $"The new data of the {schemaName} content." |
|||
}); |
|||
|
|||
Description = $"The result of the {schemaName} mutation"; |
|||
} |
|||
|
|||
private static IFieldResolver Resolve(Func<ContentDataChangedResult, object> action) |
|||
{ |
|||
return new FuncFieldResolver<ContentDataChangedResult, object>(c => action(c.Source)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,74 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using GraphQL.Resolvers; |
|||
using GraphQL.Types; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class ContentDataGraphInputType : InputObjectGraphType |
|||
{ |
|||
public ContentDataGraphInputType(IGraphModel model, ISchemaEntity schema) |
|||
{ |
|||
var schemaType = schema.TypeName(); |
|||
var schemaName = schema.DisplayName(); |
|||
|
|||
Name = $"{schemaType}InputDto"; |
|||
|
|||
foreach (var field in schema.SchemaDef.Fields.Where(x => !x.IsHidden)) |
|||
{ |
|||
var inputType = model.GetInputGraphType(field); |
|||
|
|||
if (inputType != null) |
|||
{ |
|||
if (field.RawProperties.IsRequired) |
|||
{ |
|||
inputType = new NonNullGraphType(inputType); |
|||
} |
|||
|
|||
var fieldName = field.RawProperties.Label.WithFallback(field.Name); |
|||
|
|||
var fieldGraphType = new InputObjectGraphType |
|||
{ |
|||
Name = $"{schemaType}Data{field.Name.ToPascalCase()}InputDto" |
|||
}; |
|||
|
|||
var partition = model.ResolvePartition(field.Partitioning); |
|||
|
|||
foreach (var partitionItem in partition) |
|||
{ |
|||
fieldGraphType.AddField(new FieldType |
|||
{ |
|||
Name = partitionItem.Key, |
|||
ResolvedType = inputType, |
|||
Resolver = null, |
|||
Description = field.RawProperties.Hints |
|||
}); |
|||
} |
|||
|
|||
fieldGraphType.Description = $"The input structure of the {fieldName} of a {schemaName} content type."; |
|||
|
|||
var fieldResolver = new FuncFieldResolver<NamedContentData, ContentFieldData>(c => c.Source.GetOrDefault(field.Name)); |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = field.Name.ToCamelCase(), |
|||
Resolver = fieldResolver, |
|||
ResolvedType = fieldGraphType, |
|||
Description = $"The {fieldName} field." |
|||
}); |
|||
} |
|||
} |
|||
|
|||
Description = $"The structure of a {schemaName} content type."; |
|||
} |
|||
} |
|||
} |
|||
@ -1,31 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GraphQL.Types; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class GeolocationInputGraphType : InputObjectGraphType |
|||
{ |
|||
public GeolocationInputGraphType() |
|||
{ |
|||
Name = "GeolocationInputDto"; |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = "latitude", |
|||
ResolvedType = AllTypes.NonNullFloat |
|||
}); |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = "longitude", |
|||
ResolvedType = AllTypes.NonNullFloat |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,66 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GraphQL.Types; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class InputFieldVisitor : IFieldVisitor<IGraphType> |
|||
{ |
|||
public static readonly InputFieldVisitor Default = new InputFieldVisitor(); |
|||
|
|||
private InputFieldVisitor() |
|||
{ |
|||
} |
|||
|
|||
public IGraphType Visit(IField<AssetsFieldProperties> field) |
|||
{ |
|||
return AllTypes.ListOfNonNullGuid; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<BooleanFieldProperties> field) |
|||
{ |
|||
return AllTypes.Boolean; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<DateTimeFieldProperties> field) |
|||
{ |
|||
return AllTypes.Date; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<GeolocationFieldProperties> field) |
|||
{ |
|||
return AllTypes.GeolocationInput; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<JsonFieldProperties> field) |
|||
{ |
|||
return AllTypes.NoopJson; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<NumberFieldProperties> field) |
|||
{ |
|||
return AllTypes.Float; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<ReferencesFieldProperties> field) |
|||
{ |
|||
return AllTypes.ListOfNonNullGuid; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<StringFieldProperties> field) |
|||
{ |
|||
return AllTypes.String; |
|||
} |
|||
|
|||
public IGraphType Visit(IField<TagsFieldProperties> field) |
|||
{ |
|||
return AllTypes.ListOfNonNullString; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using GraphQL.Resolvers; |
|||
using GraphQL.Types; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|||
{ |
|||
public sealed class NestedGraphType : ObjectGraphType<JObject> |
|||
{ |
|||
public NestedGraphType(IGraphModel model, ISchemaEntity schema, IArrayField field) |
|||
{ |
|||
var schemaType = schema.TypeName(); |
|||
var schemaName = schema.DisplayName(); |
|||
|
|||
var fieldType = field.TypeName(); |
|||
var fieldName = field.DisplayName(); |
|||
|
|||
Name = $"{schemaType}{fieldName}ChildDto"; |
|||
|
|||
foreach (var nestedField in field.Fields.Where(x => !x.IsHidden)) |
|||
{ |
|||
var fieldInfo = model.GetGraphType(schema, nestedField); |
|||
|
|||
if (fieldInfo.ResolveType != null) |
|||
{ |
|||
var resolver = new FuncFieldResolver<object>(c => |
|||
{ |
|||
if (((JObject)c.Source).TryGetValue(nestedField.Name, out var value)) |
|||
{ |
|||
return fieldInfo.Resolver(value, c); |
|||
} |
|||
else |
|||
{ |
|||
return fieldInfo; |
|||
} |
|||
}); |
|||
|
|||
AddField(new FieldType |
|||
{ |
|||
Name = nestedField.Name.ToCamelCase(), |
|||
Resolver = resolver, |
|||
ResolvedType = fieldInfo.ResolveType, |
|||
Description = $"The {fieldName}/{nestedField.DisplayName()} nested field." |
|||
}); |
|||
} |
|||
} |
|||
|
|||
Description = $"The structure of the {schemaName}.{fieldName} nested schema."; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GraphQL.Language.AST; |
|||
using GraphQL.Types; |
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils |
|||
{ |
|||
public sealed class JsonConverter : IAstFromValueConverter |
|||
{ |
|||
public static readonly JsonConverter Instance = new JsonConverter(); |
|||
|
|||
private JsonConverter() |
|||
{ |
|||
} |
|||
|
|||
public IValue Convert(object value, IGraphType type) |
|||
{ |
|||
return new JsonValue(value as JObject); |
|||
} |
|||
|
|||
public bool Matches(object value, IGraphType type) |
|||
{ |
|||
return value is JObject; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GraphQL.Language.AST; |
|||
using GraphQL.Types; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils |
|||
{ |
|||
public sealed class JsonGraphType : ScalarGraphType |
|||
{ |
|||
public JsonGraphType() |
|||
{ |
|||
Name = "Json"; |
|||
|
|||
Description = "Unstructured Json object"; |
|||
} |
|||
|
|||
public override object Serialize(object value) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
public override object ParseValue(object value) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
public override object ParseLiteral(IValue value) |
|||
{ |
|||
if (value is JsonValue jsonGraphType) |
|||
{ |
|||
return jsonGraphType.Value; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GraphQL.Language.AST; |
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils |
|||
{ |
|||
public sealed class JsonValue : ValueNode<JObject> |
|||
{ |
|||
public JsonValue(JObject value) |
|||
{ |
|||
Value = value; |
|||
} |
|||
|
|||
protected override bool Equals(ValueNode<JObject> node) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Schemas.Commands |
|||
{ |
|||
public sealed class CreateSchemaNestedField |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public bool IsHidden { get; set; } |
|||
|
|||
public bool IsDisabled { get; set; } |
|||
|
|||
public FieldProperties Properties { get; set; } |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue