Browse Source

GraphQL improvements.

pull/422/head
Sebastian 7 years ago
parent
commit
46d360f7df
  1. 50
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs
  2. 2
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs
  3. 15
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs
  4. 104
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs
  5. 26
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentInterfaceGraphType.cs
  6. 11
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentUnionGraphType.cs
  7. 12
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs
  8. 1
      src/Squidex.Domain.Users/UserManagerExtensions.cs
  9. 42
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs
  10. 14
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ReferencesFieldPropertiesDto.cs
  11. 105
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  12. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

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

@ -28,9 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
public sealed class GraphQLModel : IGraphModel public sealed class GraphQLModel : IGraphModel
{ {
private readonly Dictionary<ISchemaEntity, ContentGraphType> contentTypes = new Dictionary<ISchemaEntity, ContentGraphType>(); private readonly Dictionary<Guid, ContentGraphType> contentTypes = new Dictionary<Guid, ContentGraphType>();
private readonly Dictionary<ISchemaEntity, ContentDataGraphType> contentDataTypes = new Dictionary<ISchemaEntity, ContentDataGraphType>();
private readonly Dictionary<Guid, ISchemaEntity> schemasById;
private readonly PartitionResolver partitionResolver; private readonly PartitionResolver partitionResolver;
private readonly IAppEntity app; private readonly IAppEntity app;
private readonly IGraphType assetType; private readonly IGraphType assetType;
@ -54,34 +52,47 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
assetType = new AssetGraphType(this); assetType = new AssetGraphType(this);
assetListType = new ListGraphType(new NonNullGraphType(assetType)); assetListType = new ListGraphType(new NonNullGraphType(assetType));
schemasById = schemas.Where(x => x.SchemaDef.IsPublished).ToDictionary(x => x.Id); var allSchemas = schemas.Where(x => x.SchemaDef.IsPublished).ToList();
graphQLSchema = BuildSchema(this, pageSizeContents, pageSizeAssets); BuildSchemas(allSchemas);
graphQLSchema = BuildSchema(this, pageSizeContents, pageSizeAssets, allSchemas);
graphQLSchema.RegisterValueConverter(JsonConverter.Instance); graphQLSchema.RegisterValueConverter(JsonConverter.Instance);
InitializeContentTypes(); InitializeContentTypes();
} }
private static GraphQLSchema BuildSchema(GraphQLModel model, int pageSizeContents, int pageSizeAssets) private void BuildSchemas(List<ISchemaEntity> allSchemas)
{ {
var schemas = model.schemasById.Values; foreach (var schema in allSchemas)
{
return new GraphQLSchema { Query = new AppQueriesGraphType(model, pageSizeContents, pageSizeAssets, schemas) }; contentTypes[schema.Id] = new ContentGraphType(schema);
}
} }
private void InitializeContentTypes() private void InitializeContentTypes()
{ {
foreach (var kvp in contentDataTypes) foreach (var contentType in contentTypes.Values)
{ {
kvp.Value.Initialize(this, kvp.Key); contentType.Initialize(this);
} }
foreach (var kvp in contentTypes) foreach (var contentType in contentTypes.Values)
{ {
kvp.Value.Initialize(this, kvp.Key, contentDataTypes[kvp.Key]); graphQLSchema.RegisterType(contentType);
} }
} }
private static GraphQLSchema BuildSchema(GraphQLModel model, int pageSizeContents, int pageSizeAssets, List<ISchemaEntity> schemas)
{
var schema = new GraphQLSchema
{
Query = new AppQueriesGraphType(model, pageSizeContents, pageSizeAssets, schemas)
};
return schema;
}
public IFieldResolver ResolveAssetUrl() public IFieldResolver ResolveAssetUrl()
{ {
var resolver = new FuncFieldResolver<IAssetEntity, object>(c => var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>
@ -137,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public (IGraphType ResolveType, ValueResolver Resolver) GetGraphType(ISchemaEntity schema, IField field, string fieldName) public (IGraphType ResolveType, ValueResolver Resolver) GetGraphType(ISchemaEntity schema, IField field, string fieldName)
{ {
return field.Accept(new QueryGraphTypeVisitor(schema, contentTypes, GetContentType, this, assetListType, fieldName)); return field.Accept(new QueryGraphTypeVisitor(schema, contentTypes, this, assetListType, fieldName));
} }
public IObjectGraphType GetAssetType() public IObjectGraphType GetAssetType()
@ -147,16 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public IObjectGraphType GetContentType(Guid schemaId) public IObjectGraphType GetContentType(Guid schemaId)
{ {
var schema = schemasById.GetOrDefault(schemaId); return contentTypes.GetOrDefault(schemaId);
if (schema == null)
{
return null;
}
contentDataTypes.GetOrAdd(schema, s => new ContentDataGraphType());
return contentTypes.GetOrAdd(schema, s => new ContentGraphType());
} }
public async Task<(object Data, object[] Errors)> ExecuteAsync(GraphQLExecutionContext context, GraphQLQuery query) public async Task<(object Data, object[] Errors)> ExecuteAsync(GraphQLExecutionContext context, GraphQLQuery query)

2
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs

@ -17,7 +17,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
public AssetGraphType(IGraphModel model) public AssetGraphType(IGraphModel model)
{ {
Name = "AssetDto"; Name = "Asset";
AddField(new FieldType AddField(new FieldType
{ {

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

@ -18,21 +18,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData> public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData>
{ {
public ContentDataGraphType(ISchemaEntity schema) public ContentDataGraphType(ISchemaEntity schema, string schemaName, string schemaType, IGraphModel model)
{ {
var schemaType = schema.TypeName();
var schemaName = schema.DisplayName();
Name = $"{schemaType}DataDto"; Name = $"{schemaType}DataDto";
Description = $"The structure of the {schemaName} content type.";
}
public void Initialize(IGraphModel model, ISchemaEntity schema)
{
var schemaType = schema.TypeName();
var schemaName = schema.DisplayName();
foreach (var (field, fieldName, typeName) in schema.SchemaDef.Fields.SafeFields()) foreach (var (field, fieldName, typeName) in schema.SchemaDef.Fields.SafeFields())
{ {
var (resolvedType, valueResolver) = model.GetGraphType(schema, field, fieldName); var (resolvedType, valueResolver) = model.GetGraphType(schema, field, fieldName);
@ -72,6 +61,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
}); });
} }
} }
Description = $"The structure of the {schemaName} content type.";
} }
private static FuncFieldResolver<object> PartitionResolver(ValueResolver valueResolver, string key) private static FuncFieldResolver<object> PartitionResolver(ValueResolver valueResolver, string key)

104
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs

@ -15,12 +15,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
public sealed class ContentGraphType : ObjectGraphType<IEnrichedContentEntity> public sealed class ContentGraphType : ObjectGraphType<IEnrichedContentEntity>
{ {
public ContentGraphType(IGraphModel model, ISchemaEntity schema, IComplexGraphType contentDataType) private readonly ISchemaEntity schema;
private readonly string schemaType;
private readonly string schemaName;
public ContentGraphType(ISchemaEntity schema)
{ {
var schemaType = schema.TypeName(); this.schema = schema;
var schemaName = schema.DisplayName();
schemaType = schema.TypeName();
schemaName = schema.DisplayName();
Name = $"{schemaType}Dto"; Name = $"{schemaType}";
AddField(new FieldType AddField(new FieldType
{ {
@ -86,90 +92,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The color status of the {schemaName} content." Description = $"The color status of the {schemaName} content."
}); });
AddField(new FieldType
{
Name = "url",
ResolvedType = AllTypes.NonNullString,
Resolver = model.ResolveContentUrl(schema),
Description = $"The url to the the {schemaName} content."
});
Interface<ContentInterfaceGraphType>(); Interface<ContentInterfaceGraphType>();
Description = $"The structure of a {schemaName} content type."; Description = $"The structure of a {schemaName} content type.";
IsTypeOf = CheckType;
} }
public void Initialize(IGraphModel model, ISchemaEntity schema, IComplexGraphType contentDataType) private bool CheckType(object value)
{ {
var schemaType = schema.TypeName(); return value is IContentEntity content && content.SchemaId?.Id == schema.Id;
var schemaName = schema.DisplayName(); }
Name = $"{schemaType}Dto";
AddField(new FieldType
{
Name = "id",
ResolvedType = AllTypes.NonNullGuid,
Resolver = Resolve(x => x.Id),
Description = $"The id of the {schemaName} content."
});
AddField(new FieldType
{
Name = "version",
ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.Version),
Description = $"The version of the {schemaName} content."
});
AddField(new FieldType
{
Name = "created",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created),
Description = $"The date and time when the {schemaName} content has been created."
});
AddField(new FieldType
{
Name = "createdBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.CreatedBy.ToString()),
Description = $"The user that has created the {schemaName} content."
});
AddField(new FieldType
{
Name = "lastModified",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified),
Description = $"The date and time when the {schemaName} content has been modified last."
});
AddField(new FieldType
{
Name = "lastModifiedBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.LastModifiedBy.ToString()),
Description = $"The user that has updated the {schemaName} content last."
});
AddField(new FieldType
{
Name = "status",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.Status.Name.ToUpperInvariant()),
Description = $"The the status of the {schemaName} content."
});
AddField(new FieldType
{
Name = "statusColor",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.StatusColor),
Description = $"The color status of the {schemaName} content."
});
public void Initialize(IGraphModel model)
{
AddField(new FieldType AddField(new FieldType
{ {
Name = "url", Name = "url",
@ -178,6 +114,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The url to the the {schemaName} content." Description = $"The url to the the {schemaName} content."
}); });
var contentDataType = new ContentDataGraphType(schema, schemaName, schemaType, model);
if (contentDataType.Fields.Any()) if (contentDataType.Fields.Any())
{ {
AddField(new FieldType AddField(new FieldType
@ -196,10 +134,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The draft data of the {schemaName} content." Description = $"The draft data of the {schemaName} content."
}); });
} }
Interface<ContentInterfaceGraphType>();
Description = $"The structure of a {schemaName} content type.";
} }
private static IFieldResolver Resolve(Func<IEnrichedContentEntity, object> action) private static IFieldResolver Resolve(Func<IEnrichedContentEntity, object> action)

26
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentInterfaceGraphType.cs

@ -5,20 +5,23 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types; using GraphQL.Types;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
public sealed class ContentInterfaceGraphType : InterfaceGraphType public sealed class ContentInterfaceGraphType : InterfaceGraphType<IContentEntity>
{ {
public ContentInterfaceGraphType() public ContentInterfaceGraphType()
{ {
Name = $"ContentInfaceDto"; Name = $"Content";
AddField(new FieldType AddField(new FieldType
{ {
Name = "id", Name = "id",
ResolvedType = AllTypes.NonNullGuid, ResolvedType = AllTypes.NonNullGuid,
Resolver = Resolve(x => x.Id),
Description = $"The id of the content." Description = $"The id of the content."
}); });
@ -26,6 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "version", Name = "version",
ResolvedType = AllTypes.NonNullInt, ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.Version),
Description = $"The version of the content." Description = $"The version of the content."
}); });
@ -33,6 +37,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "created", Name = "created",
ResolvedType = AllTypes.NonNullDate, ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created),
Description = $"The date and time when the content has been created." Description = $"The date and time when the content has been created."
}); });
@ -40,6 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "createdBy", Name = "createdBy",
ResolvedType = AllTypes.NonNullString, ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.CreatedBy.ToString()),
Description = $"The user that has created the content." Description = $"The user that has created the content."
}); });
@ -47,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "lastModified", Name = "lastModified",
ResolvedType = AllTypes.NonNullDate, ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified),
Description = $"The date and time when the content has been modified last." Description = $"The date and time when the content has been modified last."
}); });
@ -54,6 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "lastModifiedBy", Name = "lastModifiedBy",
ResolvedType = AllTypes.NonNullString, ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.LastModifiedBy.ToString()),
Description = $"The user that has updated the content last." Description = $"The user that has updated the content last."
}); });
@ -61,6 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "status", Name = "status",
ResolvedType = AllTypes.NonNullString, ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.Status.Name.ToUpperInvariant()),
Description = $"The the status of the content." Description = $"The the status of the content."
}); });
@ -68,17 +77,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
Name = "statusColor", Name = "statusColor",
ResolvedType = AllTypes.NonNullString, ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.StatusColor),
Description = $"The color status of the content." Description = $"The color status of the content."
}); });
AddField(new FieldType
{
Name = "url",
ResolvedType = AllTypes.NonNullString,
Description = $"The url to the the content."
});
Description = $"The structure of all content types."; Description = $"The structure of all content types.";
} }
private static IFieldResolver Resolve(Func<IEnrichedContentEntity, object> action)
{
return new FuncFieldResolver<IEnrichedContentEntity, object>(c => action(c.Source));
}
} }
} }

11
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ReferenceGraphType.cs → src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentUnionGraphType.cs

@ -9,16 +9,15 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using GraphQL.Types; using GraphQL.Types;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
public sealed class ReferenceGraphType : UnionGraphType public sealed class ContentUnionGraphType : UnionGraphType
{ {
private readonly Dictionary<Guid, IObjectGraphType> types = new Dictionary<Guid, IObjectGraphType>(); private readonly Dictionary<Guid, IObjectGraphType> types = new Dictionary<Guid, IObjectGraphType>();
public ReferenceGraphType(string fieldName, IDictionary<ISchemaEntity, ContentGraphType> schemaTypes, IEnumerable<Guid> schemaIds, Func<Guid, IObjectGraphType> schemaResolver) public ContentUnionGraphType(string fieldName, Dictionary<Guid, ContentGraphType> schemaTypes, IEnumerable<Guid> schemaIds)
{ {
Name = $"{fieldName}ReferenceUnionDto"; Name = $"{fieldName}ReferenceUnionDto";
@ -26,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
foreach (var schemaId in schemaIds) foreach (var schemaId in schemaIds)
{ {
var schemaType = schemaResolver(schemaId); var schemaType = schemaTypes.GetOrDefault(schemaId);
if (schemaType != null) if (schemaType != null)
{ {
@ -38,7 +37,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
foreach (var schemaType in schemaTypes) foreach (var schemaType in schemaTypes)
{ {
types[schemaType.Key.Id] = schemaType.Value; types[schemaType.Key] = schemaType.Value;
} }
} }
@ -51,7 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
if (value is IContentEntity content) if (value is IContentEntity content)
{ {
return types.GetOrDefault(content.Id); return types.GetOrDefault(content.SchemaId.Id);
} }
return null; return null;

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

@ -11,6 +11,7 @@ using System.Linq;
using GraphQL.Types; using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
@ -20,16 +21,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
public sealed class QueryGraphTypeVisitor : IFieldVisitor<(IGraphType ResolveType, ValueResolver Resolver)> public sealed class QueryGraphTypeVisitor : IFieldVisitor<(IGraphType ResolveType, ValueResolver Resolver)>
{ {
private static readonly ValueResolver NoopResolver = (value, c) => value; private static readonly ValueResolver NoopResolver = (value, c) => value;
private readonly Dictionary<Guid, ContentGraphType> schemaTypes;
private readonly ISchemaEntity schema; private readonly ISchemaEntity schema;
private readonly Func<Guid, IObjectGraphType> schemaResolver;
private readonly IDictionary<ISchemaEntity, ContentGraphType> schemaTypes;
private readonly IGraphModel model; private readonly IGraphModel model;
private readonly IGraphType assetListType; private readonly IGraphType assetListType;
private readonly string fieldName; private readonly string fieldName;
public QueryGraphTypeVisitor(ISchemaEntity schema, public QueryGraphTypeVisitor(ISchemaEntity schema,
IDictionary<ISchemaEntity, ContentGraphType> schemaTypes, Dictionary<Guid, ContentGraphType> schemaTypes,
Func<Guid, IObjectGraphType> schemaResolver,
IGraphModel model, IGraphModel model,
IGraphType assetListType, IGraphType assetListType,
string fieldName) string fieldName)
@ -37,7 +36,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
this.model = model; this.model = model;
this.assetListType = assetListType; this.assetListType = assetListType;
this.schema = schema; this.schema = schema;
this.schemaResolver = schemaResolver;
this.schemaTypes = schemaTypes; this.schemaTypes = schemaTypes;
this.fieldName = fieldName; this.fieldName = fieldName;
} }
@ -123,11 +121,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
private (IGraphType ResolveType, ValueResolver Resolver) ResolveReferences(IField<ReferencesFieldProperties> field) private (IGraphType ResolveType, ValueResolver Resolver) ResolveReferences(IField<ReferencesFieldProperties> field)
{ {
IGraphType contentType = schemaResolver(field.Properties.SingleId()); IGraphType contentType = schemaTypes.GetOrDefault(field.Properties.SingleId());
if (contentType == null) if (contentType == null)
{ {
var union = new ReferenceGraphType(fieldName, schemaTypes, field.Properties.SchemaIds, schemaResolver); var union = new ContentUnionGraphType(fieldName, schemaTypes, field.Properties.SchemaIds);
if (!union.PossibleTypes.Any()) if (!union.PossibleTypes.Any())
{ {

1
src/Squidex.Domain.Users/UserManagerExtensions.cs

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;

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

@ -30,6 +30,15 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters
return SimpleMapper.Map(properties, new ArrayFieldPropertiesDto()); return SimpleMapper.Map(properties, new ArrayFieldPropertiesDto());
} }
public FieldPropertiesDto Visit(AssetsFieldProperties properties)
{
var result = SimpleMapper.Map(properties, new AssetsFieldPropertiesDto());
result.AllowedExtensions = properties.AllowedExtensions?.ToArray();
return result;
}
public FieldPropertiesDto Visit(BooleanFieldProperties properties) public FieldPropertiesDto Visit(BooleanFieldProperties properties)
{ {
return SimpleMapper.Map(properties, new BooleanFieldPropertiesDto()); return SimpleMapper.Map(properties, new BooleanFieldPropertiesDto());
@ -50,50 +59,45 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters
return SimpleMapper.Map(properties, new JsonFieldPropertiesDto()); return SimpleMapper.Map(properties, new JsonFieldPropertiesDto());
} }
public FieldPropertiesDto Visit(ReferencesFieldProperties properties) public FieldPropertiesDto Visit(NumberFieldProperties properties)
{
return SimpleMapper.Map(properties, new ReferencesFieldPropertiesDto());
}
public FieldPropertiesDto Visit(UIFieldProperties properties)
{
return SimpleMapper.Map(properties, new UIFieldPropertiesDto());
}
public FieldPropertiesDto Visit(TagsFieldProperties properties)
{ {
var result = SimpleMapper.Map(properties, new TagsFieldPropertiesDto()); var result = SimpleMapper.Map(properties, new NumberFieldPropertiesDto());
result.AllowedValues = properties.AllowedValues?.ToArray(); result.AllowedValues = properties.AllowedValues?.ToArray();
return result; return result;
} }
public FieldPropertiesDto Visit(AssetsFieldProperties properties) public FieldPropertiesDto Visit(ReferencesFieldProperties properties)
{ {
var result = SimpleMapper.Map(properties, new AssetsFieldPropertiesDto()); var result = SimpleMapper.Map(properties, new ReferencesFieldPropertiesDto());
result.AllowedExtensions = properties.AllowedExtensions?.ToArray(); result.SchemaIds = properties.SchemaIds?.ToArray();
return result; return result;
} }
public FieldPropertiesDto Visit(NumberFieldProperties properties) public FieldPropertiesDto Visit(StringFieldProperties properties)
{ {
var result = SimpleMapper.Map(properties, new NumberFieldPropertiesDto()); var result = SimpleMapper.Map(properties, new StringFieldPropertiesDto());
result.AllowedValues = properties.AllowedValues?.ToArray(); result.AllowedValues = properties.AllowedValues?.ToArray();
return result; return result;
} }
public FieldPropertiesDto Visit(StringFieldProperties properties) public FieldPropertiesDto Visit(TagsFieldProperties properties)
{ {
var result = SimpleMapper.Map(properties, new StringFieldPropertiesDto()); var result = SimpleMapper.Map(properties, new TagsFieldPropertiesDto());
result.AllowedValues = properties.AllowedValues?.ToArray(); result.AllowedValues = properties.AllowedValues?.ToArray();
return result; return result;
} }
public FieldPropertiesDto Visit(UIFieldProperties properties)
{
return SimpleMapper.Map(properties, new UIFieldPropertiesDto());
}
} }
} }

14
src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ReferencesFieldPropertiesDto.cs

@ -7,6 +7,7 @@
using System; using System;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -39,13 +40,20 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
public ReferencesFieldEditor Editor { get; set; } public ReferencesFieldEditor Editor { get; set; }
/// <summary> /// <summary>
/// The id of the referenced schema. /// The id of the referenced schemas.
/// </summary> /// </summary>
public Guid SchemaId { get; set; } public Guid[] SchemaIds { get; set; }
public override FieldProperties ToProperties() public override FieldProperties ToProperties()
{ {
return SimpleMapper.Map(this, new ReferencesFieldProperties()); var result = SimpleMapper.Map(this, new ReferencesFieldProperties());
if (SchemaIds != null)
{
result.SchemaIds = ReadOnlyCollection.Create(SchemaIds);
}
return result;
} }
} }
} }

105
tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -18,6 +18,95 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
public class GraphQLQueriesTests : GraphQLTestBase public class GraphQLQueriesTests : GraphQLTestBase
{ {
[Fact]
public async Task Should_introspect()
{
const string query = @"
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation
onFragment
onField
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}";
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query, OperationName = "IntrospectionQuery" });
var json = serializer.Serialize(result.Response, true);
Assert.NotEmpty(json);
}
[Theory] [Theory]
[InlineData(null)] [InlineData(null)]
[InlineData("")] [InlineData("")]
@ -775,7 +864,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public async Task Should_also_fetch_referenced_contents_when_field_is_included_in_query() public async Task Should_also_fetch_referenced_contents_when_field_is_included_in_query()
{ {
var contentRefId = Guid.NewGuid(); var contentRefId = Guid.NewGuid();
var contentRef = CreateRefContent(contentRefId, "ref1-field", "ref1"); var contentRef = CreateRefContent(schemaRefId1, contentRefId, "ref1-field", "ref1");
var contentId = Guid.NewGuid(); var contentId = Guid.NewGuid();
var content = CreateContent(contentId, contentRefId, Guid.Empty); var content = CreateContent(contentId, contentRefId, Guid.Empty);
@ -845,7 +934,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public async Task Should_also_fetch_union_contents_when_field_is_included_in_query() public async Task Should_also_fetch_union_contents_when_field_is_included_in_query()
{ {
var contentRefId = Guid.NewGuid(); var contentRefId = Guid.NewGuid();
var contentRef = CreateRefContent(contentRefId, "ref1-field", "ref1"); var contentRef = CreateRefContent(schemaRefId1, contentRefId, "ref1-field", "ref1");
var contentId = Guid.NewGuid(); var contentId = Guid.NewGuid();
var content = CreateContent(contentId, contentRefId, Guid.Empty); var content = CreateContent(contentId, contentRefId, Guid.Empty);
@ -857,15 +946,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
data { data {
myUnion { myUnion {
iv { iv {
id ... on Content {
__typename id
...MyRefSchema1Dto { }
... on MyRefSchema1 {
data { data {
ref1Field { ref1Field {
iv iv
} }
} }
} }
__typename
} }
} }
} }
@ -889,14 +980,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
id = content.Id, id = content.Id,
data = new data = new
{ {
myReferences = new myUnion = new
{ {
iv = new[] iv = new[]
{ {
new new
{ {
id = contentRefId, id = contentRefId,
__typename = "MyRefSchema1Dto",
data = new data = new
{ {
ref1Field = new ref1Field = new
@ -904,6 +994,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
iv = "ref1" iv = "ref1"
} }
}, },
__typename = "MyRefSchema1"
} }
} }
} }

6
tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

@ -109,7 +109,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
sut = CreateSut(); sut = CreateSut();
} }
protected static IEnrichedContentEntity CreateContent(Guid id, Guid refId, Guid assetId, NamedContentData data = null, NamedContentData dataDraft = null) protected IEnrichedContentEntity CreateContent(Guid id, Guid refId, Guid assetId, NamedContentData data = null, NamedContentData dataDraft = null)
{ {
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
@ -173,6 +173,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
LastModifiedBy = new RefToken(RefTokenType.Subject, "user2"), LastModifiedBy = new RefToken(RefTokenType.Subject, "user2"),
Data = data, Data = data,
DataDraft = dataDraft, DataDraft = dataDraft,
SchemaId = schemaId,
Status = Status.Draft, Status = Status.Draft,
StatusColor = "red" StatusColor = "red"
}; };
@ -180,7 +181,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return content; return content;
} }
protected static IEnrichedContentEntity CreateRefContent(Guid id, string field, string value) protected static IEnrichedContentEntity CreateRefContent(NamedId<Guid> schemaId, Guid id, string field, string value)
{ {
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
@ -200,6 +201,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
LastModifiedBy = new RefToken(RefTokenType.Subject, "user2"), LastModifiedBy = new RefToken(RefTokenType.Subject, "user2"),
Data = data, Data = data,
DataDraft = data, DataDraft = data,
SchemaId = schemaId,
Status = Status.Draft, Status = Status.Draft,
StatusColor = "red" StatusColor = "red"
}; };

Loading…
Cancel
Save