Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes building the GraphQL schema. #16184

Merged
merged 5 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using GraphQL.Resolvers;
using GraphQL.Types;
Expand All @@ -12,7 +13,7 @@ namespace OrchardCore.ContentFields.GraphQL.Fields
{
public class ContentFieldsProvider : IContentFieldProvider
{
private static readonly Dictionary<string, FieldTypeDescriptor> _contentFieldTypeMappings = new()
private static readonly FrozenDictionary<string, FieldTypeDescriptor> _contentFieldTypeMappings = new Dictionary<string, FieldTypeDescriptor>()
{
{
nameof(BooleanField),
Expand Down Expand Up @@ -84,16 +85,15 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldAccessor = field => ((MultiTextField)field).Values,
}
}
};
}.ToFrozenDictionary();

public FieldType GetField(ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName)
public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName)
{
if (!_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var value))
if (!_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldDescriptor))
{
return null;
}

var fieldDescriptor = value;
return new FieldType
{
Name = customFieldName ?? field.Name,
Expand All @@ -116,7 +116,7 @@ public FieldType GetField(ContentPartFieldDefinition field, string namedPartTech
};
}

public bool HasField(ContentPartFieldDefinition field) => _contentFieldTypeMappings.ContainsKey(field.FieldDefinition.Name);
public bool HasField(ISchema schema, ContentPartFieldDefinition field) => _contentFieldTypeMappings.ContainsKey(field.FieldDefinition.Name);

private sealed class FieldTypeDescriptor
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.GraphQL.Queries.Types;
using OrchardCore.ContentManagement.Metadata.Models;
Expand All @@ -13,25 +10,21 @@ namespace OrchardCore.ContentFields.GraphQL.Fields
{
public class ObjectGraphTypeFieldProvider : IContentFieldProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private static readonly ConcurrentDictionary<string, IObjectGraphType> _partObjectGraphTypes = new();

public ObjectGraphTypeFieldProvider(IHttpContextAccessor httpContextAccessor)
public ObjectGraphTypeFieldProvider()
{
_httpContextAccessor = httpContextAccessor;
}

public FieldType GetField(ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName)
public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName)
{
var queryGraphType = GetObjectGraphType(field);
var queryGraphType = GetObjectGraphType(schema, field);

if (queryGraphType != null)
{
return new FieldType
{
Name = customFieldName ?? field.Name,
Description = field.FieldDefinition.Name,
Type = queryGraphType.GetType(),
ResolvedType = queryGraphType,
Resolver = new FuncFieldResolver<ContentElement, ContentElement>(context =>
{
var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType.GetGenericArguments().First();
Expand All @@ -51,16 +44,11 @@ public FieldType GetField(ContentPartFieldDefinition field, string namedPartTech
return null;
}

private IObjectGraphType GetObjectGraphType(ContentPartFieldDefinition field)
{
var serviceProvider = _httpContextAccessor.HttpContext.RequestServices;

return _partObjectGraphTypes.GetOrAdd(field.FieldDefinition.Name,
partName => serviceProvider.GetService<IEnumerable<IObjectGraphType>>()?
.FirstOrDefault(x => x.GetType().BaseType.GetGenericArguments().First().Name == partName)
);
}
public bool HasField(ISchema schema, ContentPartFieldDefinition field) => GetObjectGraphType(schema, field) != null;

public bool HasField(ContentPartFieldDefinition field) => GetObjectGraphType(field) != null;
private static IObjectGraphType GetObjectGraphType(ISchema schema, ContentPartFieldDefinition field) =>
schema.AdditionalTypeInstances
.OfType<IObjectGraphType>()
.FirstOrDefault(x => x.GetType().BaseType.GetGenericArguments().First().Name == field.FieldDefinition.Name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IContentFieldProvider, ObjectGraphTypeFieldProvider>();
services.AddScoped<IContentFieldProvider, ContentFieldsProvider>();
services.AddTransient<IContentFieldProvider, ObjectGraphTypeFieldProvider>();
services.AddTransient<IContentFieldProvider, ContentFieldsProvider>();

services.AddObjectGraphType<LinkField, LinkFieldQueryObjectType>();
services.AddObjectGraphType<HtmlField, HtmlFieldQueryObjectType>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace OrchardCore.Flows.GraphQL
{
public class FlowMetadataContentTypeBuilder : IContentTypeBuilder
{
public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
{
if (contentTypeDefinition.GetStereotype() != "Widget")
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace OrchardCore.Menu.GraphQL
{
public class MenuItemContentTypeBuilder : IContentTypeBuilder
{
public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
{
if (contentTypeDefinition.GetStereotype() != "MenuItem")
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public async Task BuildAsync(ISchema schema)

foreach (var builder in contentTypeBuilders)
{
builder.Build(query, typeDefinition, typeType);
builder.Build(schema, query, typeDefinition, typeType);
}

// Limit queries to standard content types or those content types that are explicitly configured.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public DynamicContentTypeBuilder(IHttpContextAccessor httpContextAccessor,
S = localizer;
}

public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
{
var serviceProvider = _httpContextAccessor.HttpContext.RequestServices;
var contentFieldProviders = serviceProvider.GetServices<IContentFieldProvider>().ToList();
Expand All @@ -53,7 +53,7 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
continue;
}

if (!(part.PartDefinition.Fields.Any(field => contentFieldProviders.Any(fieldProvider => fieldProvider.HasField(field)))))
if (!(part.PartDefinition.Fields.Any(field => contentFieldProviders.Any(fieldProvider => fieldProvider.HasField(schema, field)))))
{
continue;
}
Expand All @@ -66,7 +66,7 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
{
var customFieldName = GraphQLContentOptions.GetFieldName(part, part.Name, field.Name);

var fieldType = fieldProvider.GetField(field, part.Name, customFieldName);
var fieldType = fieldProvider.GetField(schema, field, part.Name, customFieldName);

if (fieldType != null)
{
Expand All @@ -92,7 +92,7 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
{
foreach (var fieldProvider in contentFieldProviders)
{
var contentFieldType = fieldProvider.GetField(field, part.Name);
var contentFieldType = fieldProvider.GetField(schema, field, part.Name);

if (contentFieldType != null && !contentItemType.HasField(contentFieldType.Name))
{
Expand Down Expand Up @@ -121,7 +121,7 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
return context.Source.Get(typeToResolve, nameToResolve);
});

field.Type(new DynamicPartGraphType(_httpContextAccessor, part));
field.Type(new DynamicPartGraphType(part));
_dynamicPartFields[partName] = field.FieldType;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
using System;
using System.Linq;
using GraphQL.Types;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.ContentManagement.Metadata.Models;

namespace OrchardCore.ContentManagement.GraphQL.Queries.Types
{
public sealed class DynamicPartGraphType : ObjectGraphType<ContentPart>
{
public DynamicPartGraphType(IHttpContextAccessor httpContextAccessor, ContentTypePartDefinition part)
private ContentTypePartDefinition _part;

public DynamicPartGraphType(ContentTypePartDefinition part)
{
Name = part.Name;
_part = part;
}

var serviceProvider = httpContextAccessor.HttpContext.RequestServices;
var contentFieldProviders = serviceProvider.GetServices<IContentFieldProvider>().ToList();

foreach (var field in part.PartDefinition.Fields)
public override void Initialize(ISchema schema)
{
if (schema is IServiceProvider serviceProvider)
{
foreach (var fieldProvider in contentFieldProviders)
var contentFieldProviders = serviceProvider.GetServices<IContentFieldProvider>().ToList();

foreach (var field in _part.PartDefinition.Fields)
{
var fieldType = fieldProvider.GetField(field, part.Name);
if (fieldType != null)
foreach (var fieldProvider in contentFieldProviders)
{
AddField(fieldType);
break;
var fieldType = fieldProvider.GetField(schema, field, _part.Name);
if (fieldType != null)
{
AddField(fieldType);
break;
}
}
}
}

// Part is not required here anymore, do not keep it alive.
_part = null;

base.Initialize(schema);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace OrchardCore.ContentManagement.GraphQL.Queries.Types
{
public interface IContentFieldProvider
{
FieldType GetField(ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName = null);
FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName = null);

bool HasField(ContentPartFieldDefinition field);
bool HasField(ISchema schema, ContentPartFieldDefinition field);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace OrchardCore.ContentManagement.GraphQL.Queries.Types
{
public interface IContentTypeBuilder
{
void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType);
void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType);
void Clear() { }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using GraphQL;
using GraphQL.Resolvers;
Expand All @@ -14,10 +12,6 @@ namespace OrchardCore.ContentManagement.GraphQL.Queries.Types
{
public class TypedContentTypeBuilder : IContentTypeBuilder
{
private static readonly ConcurrentDictionary<string, Type> _partTypes = new();
private static readonly ConcurrentDictionary<string, IObjectGraphType> _partObjectGraphTypes = new();
private static readonly ConcurrentDictionary<string, IInputObjectGraphType> _partInputObjectGraphTypes = new();

private readonly IHttpContextAccessor _httpContextAccessor;
private readonly GraphQLContentOptions _contentOptions;

Expand All @@ -28,7 +22,7 @@ public TypedContentTypeBuilder(IHttpContextAccessor httpContextAccessor,
_contentOptions = contentOptionsAccessor.Value;
}

public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType)
{
var serviceProvider = _httpContextAccessor.HttpContext.RequestServices;
var typeActivator = serviceProvider.GetService<ITypeActivatorFactory<ContentPart>>();
Expand All @@ -38,9 +32,6 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
return;
}

var queryObjectGraphTypes = serviceProvider.GetServices<IObjectGraphType>();
var queryInputGraphTypes = serviceProvider.GetServices<IInputObjectGraphType>();

foreach (var part in contentTypeDefinition.Parts)
{
if (_contentOptions.ShouldSkip(part))
Expand All @@ -56,14 +47,8 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
continue;
}

var partType = _partTypes.GetOrAdd(part.PartDefinition.Name, key => typeActivator.GetTypeActivator(key).Type);
var queryGraphType = _partObjectGraphTypes
.GetOrAdd(part.PartDefinition.Name,
partName =>
{
return queryObjectGraphTypes.FirstOrDefault(x => x.GetType().BaseType.GetGenericArguments().First().Name == partName);
}
);
var queryGraphType = schema.AdditionalTypeInstances.OfType<IObjectGraphType>()
.FirstOrDefault(x => x.GetType().BaseType.GetGenericArguments().First().Name == part.PartDefinition.Name);

var collapsePart = _contentOptions.ShouldCollapse(part);

Expand All @@ -78,6 +63,7 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
continue;
}

var partType = typeActivator.GetTypeActivator(part.PartDefinition.Name).Type;
var rolledUpField = new FieldType
{
Name = field.Name,
Expand Down Expand Up @@ -124,10 +110,8 @@ public void Build(FieldType contentQuery, ContentTypeDefinition contentTypeDefin
}
}

var inputGraphTypeResolved = _partInputObjectGraphTypes.GetOrAdd(part.PartDefinition.Name, partName =>
{
return queryInputGraphTypes.FirstOrDefault(x => x.GetType().BaseType.GetGenericArguments().FirstOrDefault()?.Name == part.PartDefinition.Name);
});
var inputGraphTypeResolved = schema.AdditionalTypeInstances.OfType<IInputObjectGraphType>()
.FirstOrDefault(x => x.GetType().BaseType.GetGenericArguments().FirstOrDefault()?.Name == part.PartDefinition.Name);

if (inputGraphTypeResolved != null)
{
Expand Down