Skip to content

Commit

Permalink
Fixes several minor issues when building the GraphQL schema (#16151)
Browse files Browse the repository at this point in the history
Co-authored-by: Hisham Bin Ateya <[email protected]>
  • Loading branch information
gvkries and hishamco authored Jun 7, 2024
1 parent 39980a1 commit 07a655b
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 56 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Linq;
using GraphQL.Types;

namespace OrchardCore.ContentManagement.GraphQL;

public static class GraphQLTypeExtensions
{
public static FieldType WithPartCollapsedMetaData(this FieldType fieldType, bool collapsed = true)
=> fieldType.WithMetaData("PartCollapsed", collapsed);

public static FieldType WithPartNameMetaData(this FieldType fieldType, string partName)
=> fieldType.WithMetaData("PartName", partName);

/// <summary>
/// Checks if the field exists in the GraphQL type in a case-insensitive way.
/// </summary>
/// <remarks>
/// <para>
/// This is the same as calling <see cref="IComplexGraphType.HasField(string)"/> but in a case-insensitive way. OC
/// fields may be added with different casings, and we want to avoid collisions even then.
/// </para>
/// <para>
/// See <see href="https://github.com/OrchardCMS/OrchardCore/pull/16151"/> and its corresponding issues for context.
/// </para>
/// </remarks>
public static bool HasFieldIgnoreCase(this IComplexGraphType graphType, string fieldName)
=> graphType.Fields.Any(field => field.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase));

private static FieldType WithMetaData(this FieldType fieldType, string name, object value)
{
// TODO: Understand if locking is the best solution to https://github.com/OrchardCMS/OrchardCore/issues/15308
lock (fieldType.Metadata)
{
fieldType.Metadata.TryAdd(name, value);
}

return fieldType;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GraphQL.Types;
Expand Down Expand Up @@ -70,7 +71,8 @@ public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition

if (fieldType != null)
{
if (_contentOptions.ShouldSkip(fieldType.Type, fieldType.Name))
if (_contentOptions.ShouldSkip(fieldType.Type, fieldType.Name) ||
contentItemType.HasFieldIgnoreCase(fieldType.Name))
{
continue;
}
Expand All @@ -84,45 +86,63 @@ public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition
else
{
// Check if another builder has already added a field for this part.
var existingField = contentItemType.GetField(partName.ToFieldName());
if (existingField != null)
var partFieldName = partName.ToFieldName();
var partFieldType = contentItemType.GetField(partFieldName);

if (partFieldType != null)
{
// Add content field types.
foreach (var field in part.PartDefinition.Fields)
// Add dynamic content field types to the static part type.
var partContentItemType = schema.AdditionalTypeInstances
.OfType<IObjectGraphType>()
.Where(type => type.GetType() == partFieldType.Type)
.FirstOrDefault();

if (partContentItemType != null)
{
foreach (var fieldProvider in contentFieldProviders)
foreach (var field in part.PartDefinition.Fields)
{
var contentFieldType = fieldProvider.GetField(schema, field, part.Name);

if (contentFieldType != null && !contentItemType.HasField(contentFieldType.Name))
foreach (var fieldProvider in contentFieldProviders)
{
contentItemType.AddField(contentFieldType);
break;
var contentFieldType = fieldProvider.GetField(schema, field, part.Name);

if (contentFieldType != null)
{
if (_contentOptions.ShouldSkip(contentFieldType.Type, contentFieldType.Name) ||
partContentItemType.HasFieldIgnoreCase(contentFieldType.Name))
{
continue;
}


partContentItemType.AddField(contentFieldType);
break;
}
}
}
}
continue;
}

if (_dynamicPartFields.TryGetValue(partName, out var fieldType))
{
contentItemType.AddField(fieldType);
}
else
{
var field = contentItemType
.Field<DynamicPartGraphType>(partName.ToFieldName())
.Description(S["Represents a {0}.", part.PartDefinition.Name])
.Resolve(context =>
{
var nameToResolve = partName;
var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType.GetGenericArguments().First();
if (_dynamicPartFields.TryGetValue(partName, out var fieldType))
{
contentItemType.AddField(fieldType);
}
else
{
var field = contentItemType
.Field<DynamicPartGraphType>(partFieldName)
.Description(S["Represents a {0}.", part.PartDefinition.Name])
.Resolve(context =>
{
var nameToResolve = partName;
var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType.GetGenericArguments().First();
return context.Source.Get(typeToResolve, nameToResolve);
});
return context.Source.Get(typeToResolve, nameToResolve);
});

field.Type(new DynamicPartGraphType(part));
_dynamicPartFields[partName] = field.FieldType;
field.Type(new DynamicPartGraphType(part));
_dynamicPartFields[partName] = field.FieldType;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Linq;
using GraphQL;
using GraphQL.Resolvers;
Expand Down Expand Up @@ -40,9 +41,10 @@ public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition
}

var partName = part.Name;
var partFieldName = partName.ToFieldName();

// Check if another builder has already added a field for this part.
if (contentItemType.HasField(partName))
if (contentItemType.HasField(partFieldName))
{
continue;
}
Expand All @@ -58,7 +60,8 @@ public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition
{
foreach (var field in queryGraphType.Fields)
{
if (_contentOptions.ShouldSkip(queryGraphType.GetType(), field.Name))
if (_contentOptions.ShouldSkip(queryGraphType.GetType(), field.Name) ||
contentItemType.HasFieldIgnoreCase(field.Name))
{
continue;
}
Expand Down Expand Up @@ -94,11 +97,11 @@ public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition
{
var field = new FieldType
{
Name = partName.ToFieldName(),
Name = partFieldName,
Type = queryGraphType.GetType(),
Description = queryGraphType.Description,
};
contentItemType.Field(partName.ToFieldName(), queryGraphType.GetType())
contentItemType.Field(partFieldName, queryGraphType.GetType())
.Description(queryGraphType.Description)
.Resolve(context =>
{
Expand Down Expand Up @@ -135,7 +138,7 @@ public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition
whereInput.AddField(new FieldType
{
Type = inputGraphTypeResolved.GetType(),
Name = partName.ToFieldName(),
Name = partFieldName,
Description = inputGraphTypeResolved.Description
}.WithPartNameMetaData(partName));
}
Expand Down
4 changes: 4 additions & 0 deletions src/docs/releases/2.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ Here are the updated signatures:

These adjustments ensure compatibility and adherence to the latest conventions within the `SectionDisplayDriver` class.

The GraphQL schema may change because fields are now always added to the correct part. Previously, additional fields may have been added to the parent content item type directly.

You may have to adjust your GraphQL queries in that case.

## Change Logs

### Azure AI Search Module
Expand Down

0 comments on commit 07a655b

Please sign in to comment.