Browse Source

GraphQL support for nested schemas.

pull/297/head
Sebastian 8 years ago
parent
commit
d899032857
  1. 2
      src/Squidex.Domain.Apps.Core.Model/Schemas/IArrayField.cs
  2. 9
      src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs
  3. 8
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs
  4. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AllowedValuesValidator.cs
  5. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AssetsValidator.cs
  6. 6
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionItemValidator.cs
  7. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionValidator.cs
  8. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs
  9. 29
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/Formatter.cs
  10. 6
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/IValidator.cs
  11. 12
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs
  12. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/PatternValidator.cs
  13. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RangeValidator.cs
  14. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ReferencesValidator.cs
  15. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RequiredStringValidator.cs
  16. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RequiredValidator.cs
  17. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/StringLengthValidator.cs
  18. 6
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs
  19. 12
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs
  20. 2
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphModel.cs
  21. 7
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphInputType.cs
  22. 16
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs
  23. 5
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs
  24. 48
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedObjectGraphType.cs
  25. 33
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs
  26. 10
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaExtensions.cs
  27. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs
  28. 6
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs
  29. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs

2
src/Squidex.Domain.Apps.Core.Model/Schemas/IArrayField.cs

@ -11,6 +11,8 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
public interface IArrayField : IField<ArrayFieldProperties>
{
IReadOnlyList<Field> Fields { get; }
IReadOnlyDictionary<long, Field> FieldsById { get; }
IReadOnlyDictionary<string, Field> FieldsByName { get; }

9
src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs

@ -7,6 +7,7 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using NJsonSchema;
using Squidex.Domain.Apps.Core.Schemas;
@ -30,14 +31,14 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema
Type = JsonObjectType.Object
};
foreach (var child in field.FieldsByName.Values)
foreach (var nestedField in field.Fields.Where(x => !x.IsHidden))
{
var childProperty = field.Accept(this);
childProperty.Description = child.RawProperties.Hints;
childProperty.IsRequired = child.RawProperties.IsRequired;
childProperty.Description = nestedField.RawProperties.Hints;
childProperty.IsRequired = nestedField.RawProperties.IsRequired;
itemSchema.Properties.Add(child.Name, childProperty);
itemSchema.Properties.Add(nestedField.Name, childProperty);
}
jsonProperty.Type = JsonObjectType.Object;

8
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs

@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
fieldsValidators[field.Key] = (!field.Value.RawProperties.IsRequired, CreateFieldValidator(field.Value, isPartial));
}
return new ObjectValidator<ContentFieldData>(fieldsValidators, isPartial, "field", DefaultFieldData);
return new ObjectValidator<ContentFieldData>(fieldsValidators, isPartial, "field", DefaultFieldData, Formatter.CombineForLanguage);
}
private IValidator CreateFieldValidator(Field field, bool isPartial)
@ -89,9 +89,11 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
fieldsValidators[partition.Key] = (partition.IsOptional, fieldValidator);
}
var type = field.Partitioning.Equals(Partitioning.Language) ? "language" : "invariant value";
var isLanguage = field.Partitioning.Equals(Partitioning.Language);
return new ObjectValidator<JToken>(fieldsValidators, isPartial, type, DefaultValue);
var type = isLanguage ? "language" : "invariant value";
return new ObjectValidator<JToken>(fieldsValidators, isPartial, type, DefaultValue, Formatter.Combine);
}
}
}

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AllowedValuesValidator.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.allowedValues = allowedValues;
}
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value == null)
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AssetsValidator.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.properties = properties;
}
public async Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value is ICollection<Guid> assetIds && assetIds.Count > 0)
{

6
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionItemValidator.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.itemValidators = itemValidators;
}
public async Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value is ICollection items && items.Count > 0)
{
@ -35,9 +35,11 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
foreach (var item in items)
{
var itemFormatter = Formatter.Combine($"[{index}]", addError);
foreach (var itemValidator in itemValidators)
{
innerTasks.Add(itemValidator.ValidateAsync(item, innerContext, Formatter.Combine($"[{index}]", addError)));
innerTasks.Add(itemValidator.ValidateAsync(item, innerContext, itemFormatter));
}
index++;

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionValidator.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.maxItems = maxItems;
}
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (!(value is ICollection items) || items.Count == 0)
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.field = field;
}
public async Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
try
{

29
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/Formatter.cs

@ -5,21 +5,40 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
public static class Formatter
{
public static ErrorFormatter Combine(string field, ErrorFormatter formatter)
private static readonly string IV = InvariantPartitioning.Instance.Master.Key;
public static AddError Combine(string field, AddError formatter)
{
return (f, m) =>
{
if (!string.IsNullOrWhiteSpace(f))
{
formatter($"{field}.{f}", m);
}
else
{
formatter(field, m);
}
};
}
public static AddError CombineForLanguage(string field, AddError formatter)
{
return (innerField, message) =>
return (f, m) =>
{
if (!string.IsNullOrWhiteSpace(innerField))
if (!string.IsNullOrWhiteSpace(f) && !string.Equals(f, IV, StringComparison.OrdinalIgnoreCase))
{
formatter($"{field}.{innerField}", message);
formatter($"{field}.{f}", m);
}
else
{
formatter(field, message);
formatter(field, m);
}
};
}

6
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/IValidator.cs

@ -9,10 +9,12 @@ using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
public delegate void ErrorFormatter(string field, string message);
public delegate void AddError(string field, string message);
public delegate AddError CombineFields(string field, AddError formatter);
public interface IValidator
{
Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError);
Task ValidateAsync(object value, ValidationContext context, AddError addError);
}
}

12
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs

@ -16,16 +16,18 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
private readonly bool isPartial;
private readonly string fieldType;
private readonly TValue fieldDefault;
private readonly CombineFields combiner;
public ObjectValidator(IDictionary<string, (bool IsOptional, IValidator Validator)> schema, bool isPartial, string fieldType, TValue fieldDefault)
public ObjectValidator(IDictionary<string, (bool IsOptional, IValidator Validator)> schema, bool isPartial, string fieldType, TValue fieldDefault, CombineFields combiner)
{
this.schema = schema;
this.combiner = combiner;
this.fieldDefault = fieldDefault;
this.fieldType = fieldType;
this.isPartial = isPartial;
}
public async Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value is IReadOnlyDictionary<string, TValue> values)
{
@ -35,7 +37,9 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
if (!schema.ContainsKey(name))
{
Formatter.Combine(name, addError)(null, $"Not a known {fieldType}.");
var fieldFormatter = combiner?.Invoke(name, addError) ?? Formatter.Combine(name, addError);
fieldFormatter(null, $"Not a known {fieldType}.");
}
}
@ -58,7 +62,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
var (isOptional, validator) = field.Value;
var fieldContext = context.Optional(isOptional);
var fieldFormatter = Formatter.Combine(name, addError);
var fieldFormatter = combiner(name, addError);
tasks.Add(validator.ValidateAsync(fieldValue, fieldContext, fieldFormatter));
}

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/PatternValidator.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
regex = new Regex("^" + pattern + "$", RegexOptions.None, Timeout);
}
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value is string stringValue)
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RangeValidator.cs

@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.max = max;
}
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value == null)
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ReferencesValidator.cs

@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.schemaId = schemaId;
}
public async Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value is ICollection<Guid> contentIds)
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RequiredStringValidator.cs

@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.validateEmptyStrings = validateEmptyStrings;
}
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (context.IsOptional || (value != null && !(value is string)))
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RequiredValidator.cs

@ -12,7 +12,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
public class RequiredValidator : IValidator
{
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value == null && !context.IsOptional)
{

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/StringLengthValidator.cs

@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
this.maxLength = maxLength;
}
public Task ValidateAsync(object value, ValidationContext context, ErrorFormatter addError)
public Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value is string stringValue && !string.IsNullOrEmpty(stringValue))
{

6
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs

@ -40,12 +40,12 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
var fieldsValidators = new Dictionary<string, (bool IsOptional, IValidator Validator)>();
foreach (var kvp in field.FieldsByName)
foreach (var nestedField in field.Fields)
{
fieldsValidators[kvp.Key] = (false, new FieldValidator(kvp.Value.Accept(this), kvp.Value));
fieldsValidators[nestedField.Name] = (false, new FieldValidator(nestedField.Accept(this), nestedField));
}
yield return new CollectionItemValidator(new ObjectValidator<JToken>(fieldsValidators, false, "field", JValue.CreateNull()));
yield return new CollectionItemValidator(new ObjectValidator<JToken>(fieldsValidators, false, "field", JValue.CreateNull(), Formatter.Combine));
}
public IEnumerable<IValidator> Visit(IField<AssetsFieldProperties> field)

12
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs

@ -12,8 +12,8 @@ using System.Threading.Tasks;
using GraphQL;
using GraphQL.Resolvers;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets;
@ -35,6 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private readonly PartitionResolver partitionResolver;
private readonly IAppEntity app;
private readonly IGraphType assetType;
private readonly IGraphType assetListType;
private readonly GraphQLSchema graphQLSchema;
public bool CanGenerateAssetSourceUrl { get; private set; }
@ -48,8 +49,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
CanGenerateAssetSourceUrl = urlGenerator.CanGenerateAssetSourceUrl;
assetType = new AssetGraphType(this);
assetListType = new ListGraphType(new NonNullGraphType(assetType));
schemasById = schemas.ToDictionary(x => x.Id);
schemaTypes = new QueryGraphTypeVisitor(GetContentType, new ListGraphType(new NonNullGraphType(assetType)));
graphQLSchema = BuildSchema(this);
@ -78,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private static (IGraphType ResolveType, IFieldResolver Resolver) ResolveDefault(IGraphType type)
{
return (type, new FuncFieldResolver<ContentFieldData, object>(c => c.Source.GetOrDefault(c.FieldName)));
return (type, new FuncFieldResolver<IReadOnlyDictionary<string, JToken>, object>(c => c.Source.GetOrDefault(c.FieldName)));
}
public IFieldResolver ResolveAssetUrl()
@ -134,9 +136,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return partitionResolver(key);
}
public (IGraphType ResolveType, IFieldResolver Resolver) GetGraphType(IField field)
public (IGraphType ResolveType, IFieldResolver Resolver) GetGraphType(ISchemaEntity schema, IField field)
{
return field.Accept(schemaTypes);
return field.Accept(new QueryGraphTypeVisitor(schema, GetContentType, this, assetListType));
}
public IGraphType GetInputGraphType(IField field)

2
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphModel.cs

@ -36,6 +36,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
IGraphType GetInputGraphType(IField field);
(IGraphType ResolveType, IFieldResolver Resolver) GetGraphType(IField field);
(IGraphType ResolveType, IFieldResolver Resolver) GetGraphType(ISchemaEntity schema, IField field);
}
}

7
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphInputType.cs

@ -5,9 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
@ -56,7 +58,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
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));
var fieldResolver = new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, JToken>>(c =>
{
return c.Source.GetOrDefault(field.Name);
});
AddField(new FieldType
{

16
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs

@ -5,9 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
@ -25,15 +27,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
foreach (var field in schema.SchemaDef.Fields.Where(x => !x.IsHidden))
{
var fieldInfo = model.GetGraphType(field);
var fieldInfo = model.GetGraphType(schema, field);
if (fieldInfo.ResolveType != null)
{
var fieldName = field.RawProperties.Label.WithFallback(field.Name);
var fieldType = field.TypeName();
var fieldName = field.DisplayName();
var fieldGraphType = new ObjectGraphType
{
Name = $"{schemaType}Data{field.Name.ToPascalCase()}Dto"
Name = $"{schemaType}Data{fieldType}Dto"
};
var partition = model.ResolvePartition(field.Partitioning);
@ -49,9 +52,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
});
}
fieldGraphType.Description = $"The structure of the {fieldName} of a {schemaName} content type.";
fieldGraphType.Description = $"The structure of the {fieldName} field of a {schemaName} content type.";
var fieldResolver = new FuncFieldResolver<NamedContentData, ContentFieldData>(c => c.Source.GetOrDefault(field.Name));
var fieldResolver = new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, JToken>>(c =>
{
return c.Source.GetOrDefault(field.Name);
});
AddField(new FieldType
{

5
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs

@ -18,6 +18,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
}
public IGraphType Visit(IArrayField field)
{
return AllTypes.NoopJson;
}
public IGraphType Visit(IField<AssetsFieldProperties> field)
{
return AllTypes.ListOfNonNullGuid;

48
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedObjectGraphType.cs

@ -0,0 +1,48 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Linq;
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 NestedObjectGraphType : ObjectGraphType<JObject>
{
public NestedObjectGraphType(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)
{
AddField(new FieldType
{
Name = nestedField.Name.ToCamelCase(),
Resolver = fieldInfo.Resolver,
ResolvedType = fieldInfo.ResolveType,
Description = $"The {fieldName}/{nestedField.DisplayName()} nested field."
});
}
}
Description = $"The structure of a {schemaName}.{fieldName} child schema.";
}
}
}

33
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs

@ -6,25 +6,36 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
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 QueryGraphTypeVisitor : IFieldVisitor<(IGraphType ResolveType, IFieldResolver Resolver)>
{
private readonly ISchemaEntity schema;
private readonly Func<Guid, IGraphType> schemaResolver;
private readonly IGraphModel model;
private readonly IGraphType assetListType;
public QueryGraphTypeVisitor(Func<Guid, IGraphType> schemaResolver, IGraphType assetListType)
public QueryGraphTypeVisitor(ISchemaEntity schema, Func<Guid, IGraphType> schemaResolver, IGraphModel model, IGraphType assetListType)
{
this.model = model;
this.assetListType = assetListType;
this.schema = schema;
this.schemaResolver = schemaResolver;
}
public (IGraphType ResolveType, IFieldResolver Resolver) Visit(IArrayField field)
{
return ResolveNested(field);
}
public (IGraphType ResolveType, IFieldResolver Resolver) Visit(IField<AssetsFieldProperties> field)
{
return ResolveAssets(assetListType);
@ -72,12 +83,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
private static (IGraphType ResolveType, IFieldResolver Resolver) ResolveDefault(IGraphType type)
{
return (type, new FuncFieldResolver<ContentFieldData, object>(c => c.Source.GetOrDefault(c.FieldName)));
return (type, new FuncFieldResolver<IReadOnlyDictionary<string, JToken>, object>(c => c.Source.GetOrDefault(c.FieldName)));
}
private static ValueTuple<IGraphType, IFieldResolver> ResolveAssets(IGraphType assetListType)
{
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
var resolver = new FuncFieldResolver<IReadOnlyDictionary<string, JToken>, object>(c =>
{
var context = (GraphQLExecutionContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);
@ -88,6 +99,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
return (assetListType, resolver);
}
private ValueTuple<IGraphType, IFieldResolver> ResolveNested(IArrayField field)
{
var resolver = new FuncFieldResolver<IReadOnlyDictionary<string, JToken>, object>(c =>
{
return c.Source.GetOrDefault(c.FieldName);
});
var schemaFieldType = new ListGraphType(new NonNullGraphType(new NestedObjectGraphType(model, schema, field)));
return (schemaFieldType, resolver);
}
private ValueTuple<IGraphType, IFieldResolver> ResolveReferences(IField field)
{
var schemaId = ((ReferencesFieldProperties)field.RawProperties).SchemaId;
@ -99,7 +122,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
return (null, null);
}
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
var resolver = new FuncFieldResolver<IReadOnlyDictionary<string, JToken>, object>(c =>
{
var context = (GraphQLExecutionContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);

10
src/Squidex.Domain.Apps.Entities/Schemas/SchemaExtensions.cs

@ -18,11 +18,21 @@ namespace Squidex.Domain.Apps.Entities.Schemas
return new NamedId<Guid>(schema.Id, schema.Name);
}
public static string TypeName(this IField field)
{
return field.Name.ToPascalCase();
}
public static string TypeName(this ISchemaEntity schema)
{
return schema.SchemaDef.Name.ToPascalCase();
}
public static string DisplayName(this IField field)
{
return field.RawProperties.Label.WithFallback(field.TypeName());
}
public static string DisplayName(this ISchemaEntity schema)
{
return schema.SchemaDef.Properties.Label.WithFallback(schema.TypeName());

5
src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs

@ -20,6 +20,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters
{
}
public FieldPropertiesDto Visit(ArrayFieldProperties properties)
{
throw new System.NotImplementedException();
}
public static FieldPropertiesDto Create(FieldProperties properties)
{
return properties.Accept(Instance);

6
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs

@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field.iv: Must be less or equals than '100'.", "my-field.iv")
new ValidationError("my-field: Must be less or equals than '100'.", "my-field")
});
}
@ -117,7 +117,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field.iv: Field is required.", "my-field.iv")
new ValidationError("my-field: Field is required.", "my-field")
});
}
@ -220,7 +220,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field.iv: Must be less or equals than '100'.", "my-field.iv")
new ValidationError("my-field: Must be less or equals than '100'.", "my-field")
});
}

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs

@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
CreateFormatter(errors));
}
private static ErrorFormatter CreateFormatter(IList<string> errors)
private static AddError CreateFormatter(IList<string> errors)
{
return (field, message) =>
{

Loading…
Cancel
Save