Browse Source

Avoid naming conflicts in graphql.

pull/632/head
Sebastian 5 years ago
parent
commit
08928ac49b
  1. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs
  2. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataGraphType.cs
  3. 173
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfos.cs
  4. 47
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs

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

@ -60,11 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
public GraphQLSchema BuildSchema(IEnumerable<ISchemaEntity> schemas)
{
var schemaInfos =
schemas
.Where(x => x.SchemaDef.IsPublished).Select(SchemaInfo.Build)
.Where(x => x.Fields.Count > 0)
.ToList();
var schemaInfos = SchemaInfo.Build(schemas).ToList();
foreach (var schemaInfo in schemaInfos)
{

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

@ -26,7 +26,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
{
var fieldGraphType = new ObjectGraphType
{
Name = fieldInfo.TypeName
Name = fieldInfo.LocalizedType
};
var partitioning = builder.ResolvePartition(((RootField)fieldInfo.Field).Partitioning);

173
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfos.cs

@ -5,79 +5,182 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Text;
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
#pragma warning disable SA1649 // File name should match first type name
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed record SchemaInfo(ISchemaEntity Schema, string TypeName, IReadOnlyList<FieldInfo> Fields)
internal sealed class SchemaInfo
{
public string DisplayName { get; set; } = Schema.DisplayName();
public ISchemaEntity Schema { get; }
public string ContentType { get; } = TypeName.SafeTypeName();
public string TypeName { get; }
public string DataType { get; } = $"{TypeName}DataDto";
public string DisplayName { get; }
public string DataInputType { get; } = $"{TypeName}DataInputDto";
public string ContentType { get; }
public string DataFlatType { get; } = $"{TypeName}FlatDataDto";
public string DataType { get; }
public string ResultType { get; } = $"{TypeName}ResultDto";
public string DataInputType { get; }
public static SchemaInfo Build(ISchemaEntity schema)
public string DataFlatType { get; }
public string ResultType { get; }
public IReadOnlyList<FieldInfo> Fields { get; }
private SchemaInfo(ISchemaEntity schema, string typeName, IReadOnlyList<FieldInfo> fields, Names names)
{
Schema = schema;
ContentType = names[typeName];
DataFlatType = names[$"{typeName}FlatDataDto"];
DataInputType = names[$"{typeName}DataInputDto"];
ResultType = names[$"{typeName}ResultDto"];
DataType = names[$"{typeName}DataDto"];
DisplayName = schema.DisplayName();
Fields = fields;
TypeName = typeName;
}
public override string ToString()
{
return TypeName;
}
public static IEnumerable<SchemaInfo> Build(IEnumerable<ISchemaEntity> schemas)
{
var typeName = schema.TypeName();
var names = new Names();
var fields =
schema.SchemaDef.Fields.SafeFields()
.Select(x => FieldInfo.Build(x.Field, x.Name, $"{typeName}{x.Type}"))
.ToList();
foreach (var schema in schemas.Where(x => x.SchemaDef.IsPublished && x.SchemaDef.Fields.Count > 0).OrderBy(x => x.Created))
{
var typeName = schema.TypeName();
var fields = FieldInfo.EmptyFields;
if (schema.SchemaDef.Fields.Count > 0)
{
var fieldNames = new Names();
fields = new List<FieldInfo>(schema.SchemaDef.Fields.Count);
foreach (var field in schema.SchemaDef.Fields)
{
fields.Add(FieldInfo.Build(field, fieldNames[field], names[$"{typeName}Data{field.TypeName()}"], names));
}
}
return new SchemaInfo(
schema,
schema.TypeName(),
fields);
yield return new SchemaInfo(schema, typeName, fields, names);
}
}
}
public sealed record FieldInfo(IField Field, string FieldName, string TypeName, IReadOnlyList<FieldInfo> Fields)
internal sealed class FieldInfo
{
private static readonly IReadOnlyList<FieldInfo> EmptyFields = new List<FieldInfo>();
public static readonly List<FieldInfo> EmptyFields = new List<FieldInfo>();
public IField Field { get; set; }
public string FieldName { get; }
public string DisplayName { get; }
public string DisplayName { get; set; } = Field.DisplayName();
public string LocalizedType { get; }
public string LocalizedType { get; } = $"{TypeName}Dto";
public string LocalizedInputType { get; }
public string LocalizedInputType { get; } = $"{TypeName}InputDto";
public string NestedType { get; }
public string NestedType { get; } = $"{TypeName}ChildDto";
public string NestedInputType { get; }
public string NestedInputType { get; } = $"{TypeName}ChildInputDto";
public string UnionType { get; }
public string UnionType { get; } = $"{TypeName}UnionDto";
public IReadOnlyList<FieldInfo> Fields { get; }
public static FieldInfo Build(IRootField rootField, string fieldName, string typeName)
private FieldInfo(IField field, string fieldName, string typeName, IReadOnlyList<FieldInfo> fields, Names names)
{
DisplayName = field.DisplayName();
Field = field;
Fields = fields;
FieldName = fieldName;
LocalizedType = names[$"{typeName}Dto"];
LocalizedInputType = names[$"{typeName}InputDto"];
NestedInputType = names[$"{typeName}ChildInputDto"];
NestedType = names[$"{typeName}ChildDto"];
UnionType = names[$"{typeName}UnionDto"];
}
public override string ToString()
{
return FieldName;
}
internal static FieldInfo Build(IRootField rootField, string fieldName, string typeName, Names names)
{
var fields = EmptyFields;
if (rootField is IArrayField arrayField)
if (rootField is IArrayField arrayField && arrayField.Fields.Count > 0)
{
fields =
arrayField.Fields.SafeFields()
.Select(x => Build(x.Field, x.Name, $"{typeName}{x.Type}"))
.ToList();
var fieldNames = new Names();
fields = new List<FieldInfo>(arrayField.Fields.Count);
foreach (var field in arrayField.Fields)
{
fields.Add(new FieldInfo(field, fieldNames[field], $"{typeName}{field.TypeName()}", EmptyFields, names));
}
}
return new FieldInfo(rootField, fieldName, typeName, fields);
return new FieldInfo(rootField, fieldName, typeName, fields, names);
}
}
internal sealed class Names
{
private readonly Dictionary<string, int> takenNames = new Dictionary<string, int>();
public string this[IField field]
{
get
{
return this[field.Name.ToCamelCase()];
}
}
public static FieldInfo Build(INestedField nestedField, string fieldName, string fieldTypeName)
public string this[string name]
{
return new FieldInfo(nestedField, fieldName, fieldTypeName, EmptyFields);
get
{
Guard.NotNullOrEmpty(name, nameof(name));
if (!char.IsLetter(name[0]))
{
name = "gql_" + name;
}
else if (name.Equals("Content", StringComparison.OrdinalIgnoreCase))
{
name = $"{name}Entity";
}
// Avoid duplicate names.
if (!takenNames.TryGetValue(name, out var offset))
{
takenNames[name] = 0;
return name;
}
takenNames[name] = ++offset;
// Add + 1 to all offset for backwars compatibility.
return $"{name}{offset + 1}";
}
}
}
}

47
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using GraphQL;
@ -15,56 +14,12 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.ObjectPool;
using Squidex.Text;
using GraphQLSchema = GraphQL.Types.Schema;
#pragma warning disable RECS0015 // If an extension method is called as static method convert it to method syntax
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class Extensions
internal static class Extensions
{
public static string SafeTypeName(this string typeName)
{
if (typeName.Equals("Content", StringComparison.Ordinal))
{
return $"{typeName}Entity";
}
return typeName;
}
public static IEnumerable<(T Field, string Name, string Type)> SafeFields<T>(this IEnumerable<T> fields) where T : IField
{
var allFields =
fields.FieldNames()
.GroupBy(x => x.Name)
.Select(g => g.Select((f, i) => (f.Field, f.Name.SafeString(i), f.Type.SafeString(i))))
.SelectMany(x => x);
return allFields;
}
private static IEnumerable<(T Field, string Name, string Type)> FieldNames<T>(this IEnumerable<T> fields) where T : IField
{
return fields.ForApi(true).Select(field => (field, CasingExtensions.ToCamelCase(field.Name), field.TypeName()));
}
private static string SafeString(this string value, int index)
{
if (value.Length > 0 && !char.IsLetter(value[0]))
{
value = "gql_" + value;
}
if (index > 0)
{
return value + (index + 1);
}
return value;
}
public static string BuildODataQuery(this IResolveFieldContext context)
{
var sb = DefaultPools.StringBuilder.Get();

Loading…
Cancel
Save