Skip to content

Commit

Permalink
Component fixes. (#1010)
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Jul 28, 2023
1 parent 283e2ba commit eb52464
Show file tree
Hide file tree
Showing 25 changed files with 612 additions and 273 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ static Builder()

public IInterfaceGraphType ComponentInterface { get; } = new ComponentInterfaceGraphType();

public FieldMap FieldMap { get; private set; }

public Builder(IAppEntity app, GraphQLOptions options)
{
partitionResolver = app.PartitionResolver();
Expand All @@ -64,7 +66,7 @@ public GraphQLSchema BuildSchema(IEnumerable<ISchemaEntity> schemas)
allSchemas.AddRange(SchemaInfo.Build(schemas, typeNames).Where(x => x.Fields.Count > 0));

// Only published normal schemas (not components are used for entities).
var normalSchemas = allSchemas.Where(x => x.Schema.SchemaDef.IsPublished && x.Schema.SchemaDef.Type != SchemaType.Component).ToList();
var normalSchemas = allSchemas.Where(IsNormalSchema).ToList();

foreach (var schemaInfo in normalSchemas)
{
Expand All @@ -74,7 +76,7 @@ public GraphQLSchema BuildSchema(IEnumerable<ISchemaEntity> schemas)
contentResultTypes[schemaInfo] = new ContentResultGraphType(contentType, schemaInfo);
}

foreach (var schemaInfo in normalSchemas)
foreach (var schemaInfo in allSchemas)
{
var componentType = new ComponentGraphType(schemaInfo);

Expand All @@ -92,6 +94,8 @@ public GraphQLSchema BuildSchema(IEnumerable<ISchemaEntity> schemas)
newSchema.Directives.Register(SharedTypes.CacheDirective);
newSchema.Directives.Register(SharedTypes.OptimizeFieldQueriesDirective);

FieldMap = new FieldMap(allSchemas);

if (normalSchemas.Any())
{
var mutations = new ApplicationMutations(this, normalSchemas);
Expand Down Expand Up @@ -132,6 +136,11 @@ public GraphQLSchema BuildSchema(IEnumerable<ISchemaEntity> schemas)
return newSchema;
}

private static bool IsNormalSchema(SchemaInfo schema)
{
return schema.Schema.SchemaDef.IsPublished && schema.Schema.SchemaDef.Type != SchemaType.Component;
}

public FieldGraphSchema GetGraphType(FieldInfo fieldInfo)
{
return fieldInfo.Field.Accept(fieldVisitor, fieldInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
// ==========================================================================

using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Json.Objects;

namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents;

internal sealed class DataInputGraphType : InputObjectGraphType
{
private readonly FieldMap fieldMap;

public DataInputGraphType(Builder builder, SchemaInfo schemaInfo)
{
// The name is used for equal comparison. Therefore it is important to treat it as readonly.
Expand Down Expand Up @@ -62,52 +61,12 @@ public DataInputGraphType(Builder builder, SchemaInfo schemaInfo)
}

Description = $"The structure of the {schemaInfo.DisplayName} data input type.";

fieldMap = builder.FieldMap;
}

public override object ParseDictionary(IDictionary<string, object?> value)
{
var result = new ContentData();

static ContentFieldData ToFieldData(IDictionary<string, object> source, IComplexGraphType type)
{
var result = new ContentFieldData();

foreach (var field in type.Fields)
{
if (source.TryGetValue(field.Name, out var value))
{
if (value is IEnumerable<object> list && field.ResolvedType?.InnerType() is IComplexGraphType nestedType)
{
var array = new JsonArray(list.Count());

foreach (var item in list)
{
if (item is JsonValue { Value: JsonObject } nested)
{
array.Add(nested);
}
}

result[field.SourceName()] = array;
}
else
{
result[field.SourceName()] = JsonGraphType.ParseJson(value);
}
}
}

return result;
}

foreach (var field in Fields)
{
if (field.ResolvedType is IComplexGraphType complexType && value.TryGetValue(field.Name, out var fieldValue) && fieldValue is IDictionary<string, object> nested)
{
result[field.SourceName()] = ToFieldData(nested, complexType);
}
}

return result;
return fieldMap.MapData(this, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
// ==========================================================================

using GraphQL.Types;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives;
using Squidex.Infrastructure.Json.Objects;

namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents;

internal sealed class NestedInputGraphType : InputObjectGraphType
{
private readonly FieldMap fieldMap;

public NestedInputGraphType(Builder builder, FieldInfo fieldInfo)
{
// The name is used for equal comparison. Therefore it is important to treat it as readonly.
Expand All @@ -38,20 +38,12 @@ public NestedInputGraphType(Builder builder, FieldInfo fieldInfo)
}

Description = $"The structure of the {fieldInfo.DisplayName} nested schema.";

fieldMap = builder.FieldMap;
}

public override object ParseDictionary(IDictionary<string, object?> value)
{
var result = JsonValue.Object();

foreach (var field in Fields)
{
if (value.TryGetValue(field.Name, out var fieldValue))
{
result[field.SourceName()] = JsonGraphType.ParseJson(fieldValue);
}
}

return new JsonValue(result);
return fieldMap.MapNested(this, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives;
using Squidex.Infrastructure.Json.Objects;

namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types;

internal sealed class FieldMap
{
private readonly Dictionary<string, Dictionary<string, string>> schemas = new Dictionary<string, Dictionary<string, string>>();

public FieldMap(IEnumerable<SchemaInfo> source)
{
foreach (var schema in source)
{
var fieldMap = schema.Fields.ToDictionary(x => x.FieldName, x => x.Field.Name);

schemas[schema.Schema.Id.ToString()] = fieldMap;
schemas[schema.Schema.SchemaDef.Name] = fieldMap;
}
}

public ContentData MapData(IInputObjectGraphType inputType, IDictionary<string, object?> source)
{
var result = new ContentData();

foreach (var field in inputType.Fields)
{
if (field.ResolvedType is IComplexGraphType complexType && source.TryGetValue(field.Name, out var value) && value is IDictionary<string, object> nested)
{
result[field.SourceName()] = MapField(nested, complexType);
}
}

return result;
}

public JsonValue MapNested(IInputObjectGraphType inputType, IDictionary<string, object?> source)
{
var result = new JsonObject(source.Count);

foreach (var field in inputType.Fields)
{
if (source.TryGetValue(field.Name, out var value))
{
result[field.SourceName()] = MapValue(JsonGraphType.ParseJson(value));
}
}

return result;
}

private ContentFieldData MapField(IDictionary<string, object> source, IComplexGraphType type)
{
var result = new ContentFieldData();

foreach (var field in type.Fields)
{
if (source.TryGetValue(field.Name, out var value))
{
result[field.SourceName()] = MapValue(JsonGraphType.ParseJson(value));
}
}

return result;
}

private JsonValue MapValue(JsonValue source)
{
switch (source.Value)
{
case JsonArray arr:
return MapArray(arr);
case JsonObject obj:
return MapObject(obj);
default:
return source;
}
}

private JsonValue MapArray(JsonArray source)
{
var result = new JsonArray(source.Count);

foreach (var value in source)
{
result.Add(MapValue(value));
}

return result;
}

private JsonValue MapObject(JsonObject source)
{
Dictionary<string, string>? fieldMap = null;

if (source.TryGetValue(Component.Discriminator, out var d1) && d1.Value is string discriminator)
{
schemas.TryGetValue(discriminator, out fieldMap);
}
else if (source.TryGetValue(Component.Descriptor, out var d2) && d2.Value is string descriptor)
{
schemas.TryGetValue(descriptor, out fieldMap);
}

if (fieldMap == null)
{
return source;
}

var result = new JsonObject(source.Count);

foreach (var (key, value) in source)
{
var sourceName = key;

if (fieldMap != null && fieldMap.TryGetValue(key, out var name))
{
sourceName = name;
}

result[sourceName] = MapValue(value);
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public sealed class JsonGraphType : JsonNoopGraphType
return ParseJson(value);
}

public static JsonValue ParseJson(object? input)
public static JsonValue ParseJson(object? input, Func<object, IReadOnlyDictionary<string, string>?>? keyMap = null)
{
keyMap ??= x => null;

switch (input)
{
case GraphQLBooleanValue booleanValue:
Expand All @@ -44,7 +46,7 @@ public static JsonValue ParseJson(object? input)

case GraphQLListValue listValue:
{
var json = new JsonArray();
var json = new JsonArray(listValue.Values?.Count ?? 0);

if (listValue.Values != null)
{
Expand All @@ -59,13 +61,22 @@ public static JsonValue ParseJson(object? input)

case GraphQLObjectValue objectValue:
{
var json = JsonValue.Object();
var json = new JsonObject(objectValue.Fields?.Count ?? 0);

if (objectValue.Fields != null)
{
var map = keyMap(objectValue);

foreach (var field in objectValue.Fields)
{
json[field.Name.ToString()] = ParseJson(field.Value);
var sourceField = field.Name.ToString();

if (map?.TryGetValue(sourceField, out var temp) == true)
{
sourceField = temp;
}

json[sourceField] = ParseJson(field.Value);
}
}

Expand All @@ -74,7 +85,7 @@ public static JsonValue ParseJson(object? input)

case IEnumerable<object> list:
{
var json = new JsonArray();
var json = new JsonArray(list.Count());

foreach (var item in list)
{
Expand All @@ -86,11 +97,23 @@ public static JsonValue ParseJson(object? input)

case IDictionary<string, object> obj:
{
var json = JsonValue.Object();
var json = new JsonObject(obj.Count);

foreach (var (key, value) in obj)
if (obj.Count > 0)
{
json[key] = ParseJson(value);
var map = keyMap(obj);

foreach (var (key, value) in obj)
{
var sourceField = key;

if (map?.TryGetValue(sourceField, out var temp) == true)
{
sourceField = temp;
}

json[sourceField] = ParseJson(value);
}
}

return json;
Expand Down
Loading

0 comments on commit eb52464

Please sign in to comment.