Browse Source

Graphql enums.

pull/848/head
Sebastian 4 years ago
parent
commit
e268e6e4b6
  1. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs
  2. 46
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldEnumType.cs
  3. 25
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldInputVisitor.cs
  4. 29
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs
  5. 5
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfo.cs
  6. 35
      backend/src/Squidex.Web/GraphQL/BufferingDocumentWriter.cs
  7. 1
      backend/src/Squidex.Web/Squidex.Web.csproj
  8. 5
      backend/src/Squidex/Config/Domain/SerializationServices.cs
  9. 26
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestSchemas.cs

6
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs

@ -24,6 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
private readonly Dictionary<SchemaInfo, ComponentGraphType> componentTypes = new Dictionary<SchemaInfo, ComponentGraphType>(ReferenceEqualityComparer.Instance); private readonly Dictionary<SchemaInfo, ComponentGraphType> componentTypes = new Dictionary<SchemaInfo, ComponentGraphType>(ReferenceEqualityComparer.Instance);
private readonly Dictionary<SchemaInfo, ContentGraphType> contentTypes = new Dictionary<SchemaInfo, ContentGraphType>(ReferenceEqualityComparer.Instance); private readonly Dictionary<SchemaInfo, ContentGraphType> contentTypes = new Dictionary<SchemaInfo, ContentGraphType>(ReferenceEqualityComparer.Instance);
private readonly Dictionary<SchemaInfo, ContentResultGraphType> contentResultTypes = new Dictionary<SchemaInfo, ContentResultGraphType>(ReferenceEqualityComparer.Instance); private readonly Dictionary<SchemaInfo, ContentResultGraphType> contentResultTypes = new Dictionary<SchemaInfo, ContentResultGraphType>(ReferenceEqualityComparer.Instance);
private readonly Dictionary<string, EnumerationGraphType> enumTypes = new Dictionary<string, EnumerationGraphType>();
private readonly FieldVisitor fieldVisitor; private readonly FieldVisitor fieldVisitor;
private readonly FieldInputVisitor fieldInputVisitor; private readonly FieldInputVisitor fieldInputVisitor;
private readonly PartitionResolver partitionResolver; private readonly PartitionResolver partitionResolver;
@ -150,6 +151,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
return componentTypes.GetOrDefault(schema); return componentTypes.GetOrDefault(schema);
} }
public EnumerationGraphType GetEnumeration<T>(string name, string prefix, IEnumerable<T> values)
{
return enumTypes.GetOrAdd(name, x => new FieldEnumType<T>(name, string prefix, values));
}
public IEnumerable<KeyValuePair<SchemaInfo, ContentGraphType>> GetAllContentTypes() public IEnumerable<KeyValuePair<SchemaInfo, ContentGraphType>> GetAllContentTypes()
{ {
return contentTypes; return contentTypes;

46
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldEnumType.cs

@ -0,0 +1,46 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Types;
using Squidex.Text;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
{
public sealed class FieldEnumType<T> : EnumerationGraphType
{
public FieldEnumType(string name, string prefix, IEnumerable<T> values)
{
Name = name;
var index = 0;
foreach (var value in values)
{
AddValue(BuildName(value, prefix, index), null, value);
index++;
}
}
private static string BuildName(T value, string prefix, int index)
{
var name = value!.ToString()!.Slugify().ToPascalCase();
if (string.IsNullOrEmpty(name))
{
name = $"{prefix}_{index}";
}
if (!char.IsLetter(name[0]))
{
name = $"{prefix}_{name}";
}
return name;
}
}
}

25
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldInputVisitor.cs

@ -69,23 +69,40 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
return AllTypes.Json; return AllTypes.Json;
} }
public IGraphType? Visit(IField<NumberFieldProperties> field, FieldInfo args) public IGraphType? Visit(IField<ReferencesFieldProperties> field, FieldInfo args)
{ {
return AllTypes.Float; return AllTypes.Strings;
} }
public IGraphType? Visit(IField<ReferencesFieldProperties> field, FieldInfo args) public IGraphType? Visit(IField<NumberFieldProperties> field, FieldInfo args)
{ {
return AllTypes.Strings; if (field.Properties?.AllowedValues?.Count > 0)
{
return builder.GetEnumeration(args.EnumName, "Number", field.Properties.AllowedValues);
}
return AllTypes.Float;
} }
public IGraphType? Visit(IField<StringFieldProperties> field, FieldInfo args) public IGraphType? Visit(IField<StringFieldProperties> field, FieldInfo args)
{ {
if (field.Properties?.AllowedValues?.Count > 0)
{
return builder.GetEnumeration(args.EnumName, "String", field.Properties.AllowedValues);
}
return AllTypes.String; return AllTypes.String;
} }
public IGraphType? Visit(IField<TagsFieldProperties> field, FieldInfo args) public IGraphType? Visit(IField<TagsFieldProperties> field, FieldInfo args)
{ {
if (field.Properties?.AllowedValues?.Count > 0)
{
var @enum = builder.GetEnumeration(args.EnumName, "Tag", field.Properties.AllowedValues);
return new ListGraphType(new NonNullGraphType(@enum));
}
return AllTypes.Strings; return AllTypes.Strings;
} }

29
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs

@ -164,17 +164,40 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<NumberFieldProperties> field, FieldInfo args) public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<NumberFieldProperties> field, FieldInfo args)
{ {
return (AllTypes.Float, JsonNumber, null); var type = AllTypes.Float;
if (field.Properties?.AllowedValues?.Count > 0)
{
type = builder.GetEnumeration(args.EnumName, "Number", field.Properties.AllowedValues);
}
return (type, JsonNumber, null);
} }
public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<StringFieldProperties> field, FieldInfo args) public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<StringFieldProperties> field, FieldInfo args)
{ {
return (AllTypes.String, JsonString, null); var type = AllTypes.String;
if (field.Properties?.AllowedValues?.Count > 0)
{
type = builder.GetEnumeration(args.EnumName, "String", field.Properties.AllowedValues);
}
return (type, JsonString, null);
} }
public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<TagsFieldProperties> field, FieldInfo args) public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<TagsFieldProperties> field, FieldInfo args)
{ {
return (AllTypes.Strings, JsonStrings, null); var type = AllTypes.Strings;
if (field.Properties?.AllowedValues?.Count > 0)
{
var @enum = builder.GetEnumeration(args.EnumName, "Tag", field.Properties.AllowedValues);
type = new ListGraphType(new NonNullGraphType(@enum));
}
return (type, JsonStrings, null);
} }
public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<ReferencesFieldProperties> field, FieldInfo args) public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField<ReferencesFieldProperties> field, FieldInfo args)

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

@ -92,6 +92,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
public string DisplayName { get; } public string DisplayName { get; }
public string EnumName { get; }
public string LocalizedType { get; } public string LocalizedType { get; }
public string LocalizedInputType { get; } public string LocalizedInputType { get; }
@ -116,8 +118,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
Fields = fields; Fields = fields;
FieldName = fieldName; FieldName = fieldName;
FieldNameDynamic = names[$"{fieldName}__Dynamic"]; FieldNameDynamic = names[$"{fieldName}__Dynamic"];
LocalizedType = names[$"{typeName}Dto"]; EnumName = names[$"{fieldName}Enum"];
LocalizedInputType = names[$"{typeName}InputDto"]; LocalizedInputType = names[$"{typeName}InputDto"];
LocalizedType = names[$"{typeName}Dto"];
NestedInputType = names[$"{typeName}ChildInputDto"]; NestedInputType = names[$"{typeName}ChildInputDto"];
NestedType = names[$"{typeName}ChildDto"]; NestedType = names[$"{typeName}ChildDto"];
ReferenceType = names[$"{typeName}UnionDto"]; ReferenceType = names[$"{typeName}UnionDto"];

35
backend/src/Squidex.Web/GraphQL/BufferingDocumentWriter.cs

@ -0,0 +1,35 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL;
using GraphQL.NewtonsoftJson;
using Microsoft.AspNetCore.WebUtilities;
using Newtonsoft.Json;
namespace Squidex.Web.GraphQL
{
public sealed class BufferingDocumentWriter : IDocumentWriter
{
private readonly DocumentWriter documentWriter;
public BufferingDocumentWriter(Action<JsonSerializerSettings> action)
{
documentWriter = new DocumentWriter(action);
}
public async Task WriteAsync<T>(Stream stream, T value,
CancellationToken cancellationToken = default)
{
await using (var bufferStream = new FileBufferingWriteStream())
{
await documentWriter.WriteAsync(bufferStream, value, cancellationToken);
await bufferStream.DrainBufferAsync(stream, cancellationToken);
}
}
}
}

1
backend/src/Squidex.Web/Squidex.Web.csproj

@ -17,6 +17,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="GraphQL.NewtonsoftJson" Version="4.7.1" />
<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="5.2.0" /> <PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="5.2.0" />
<PackageReference Include="Lazy.Fody" Version="1.11.0" PrivateAssets="all" /> <PackageReference Include="Lazy.Fody" Version="1.11.0" PrivateAssets="all" />
<PackageReference Include="Meziantou.Analyzer" Version="1.0.694"> <PackageReference Include="Meziantou.Analyzer" Version="1.0.694">

5
backend/src/Squidex/Config/Domain/SerializationServices.cs

@ -30,6 +30,7 @@ using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.Queries.Json; using Squidex.Infrastructure.Queries.Json;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web.GraphQL;
using IGraphQLBuilder = GraphQL.DI.IGraphQLBuilder; using IGraphQLBuilder = GraphQL.DI.IGraphQLBuilder;
namespace Squidex.Config.Domain namespace Squidex.Config.Domain
@ -128,9 +129,9 @@ namespace Squidex.Config.Domain
{ {
var errorInfoProvider = c.GetRequiredService<IErrorInfoProvider>(); var errorInfoProvider = c.GetRequiredService<IErrorInfoProvider>();
return new DocumentWriter(options => return new BufferingDocumentWriter(options =>
{ {
options.ContractResolver = new ExecutionResultContractResolver(new ErrorInfoProvider()); options.ContractResolver = new ExecutionResultContractResolver(errorInfoProvider);
options.Converters.Add(new JsonValueConverter()); options.Converters.Add(new JsonValueConverter());
options.Converters.Add(new WriteonlyGeoJsonConverter()); options.Converters.Add(new WriteonlyGeoJsonConverter());

26
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestSchemas.cs

@ -45,26 +45,32 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
new StringFieldProperties()) new StringFieldProperties())
.AddString(3, "my-localized-string", Partitioning.Language, .AddString(3, "my-localized-string", Partitioning.Language,
new StringFieldProperties()) new StringFieldProperties())
.AddNumber(4, "my-number", Partitioning.Invariant, .AddString(4, "my-string-enum", Partitioning.Invariant,
new StringFieldProperties { AllowedValues = ReadonlyList.Create("A", "B", "C") })
.AddNumber(5, "my-number", Partitioning.Invariant,
new NumberFieldProperties()) new NumberFieldProperties())
.AddAssets(5, "my-assets", Partitioning.Invariant, .AddNumber(6, "my-number-enum", Partitioning.Invariant,
new NumberFieldProperties { AllowedValues = ReadonlyList.Create(1.0, 2.0, 3.0) })
.AddAssets(7, "my-assets", Partitioning.Invariant,
new AssetsFieldProperties()) new AssetsFieldProperties())
.AddBoolean(6, "my-boolean", Partitioning.Invariant, .AddBoolean(8, "my-boolean", Partitioning.Invariant,
new BooleanFieldProperties()) new BooleanFieldProperties())
.AddDateTime(7, "my-datetime", Partitioning.Invariant, .AddDateTime(9, "my-datetime", Partitioning.Invariant,
new DateTimeFieldProperties()) new DateTimeFieldProperties())
.AddReferences(8, "my-references", Partitioning.Invariant, .AddReferences(10, "my-references", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = Ref1Id.Id }) new ReferencesFieldProperties { SchemaId = Ref1Id.Id })
.AddReferences(9, "my-union", Partitioning.Invariant, .AddReferences(11, "my-union", Partitioning.Invariant,
new ReferencesFieldProperties()) new ReferencesFieldProperties())
.AddGeolocation(10, "my-geolocation", Partitioning.Invariant, .AddGeolocation(12, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()) new GeolocationFieldProperties())
.AddComponent(11, "my-component", Partitioning.Invariant, .AddComponent(13, "my-component", Partitioning.Invariant,
new ComponentFieldProperties { SchemaId = Ref1Id.Id }) new ComponentFieldProperties { SchemaId = Ref1Id.Id })
.AddComponents(12, "my-components", Partitioning.Invariant, .AddComponents(14, "my-components", Partitioning.Invariant,
new ComponentsFieldProperties { SchemaIds = ReadonlyList.Create(Ref1.Id, Ref2.Id) }) new ComponentsFieldProperties { SchemaIds = ReadonlyList.Create(Ref1.Id, Ref2.Id) })
.AddTags(13, "my-tags", Partitioning.Invariant, .AddTags(15, "my-tags", Partitioning.Invariant,
new TagsFieldProperties()) new TagsFieldProperties())
.AddTags(16, "my-tags-enum", Partitioning.Invariant,
new TagsFieldProperties { AllowedValues = ReadonlyList.Create("A", "B", "C") })
.AddArray(100, "my-array", Partitioning.Invariant, f => f .AddArray(100, "my-array", Partitioning.Invariant, f => f
.AddBoolean(121, "nested-boolean", .AddBoolean(121, "nested-boolean",
new BooleanFieldProperties()) new BooleanFieldProperties())

Loading…
Cancel
Save