Skip to content

Commit

Permalink
The type trimming now correctly handles executable directives (#2605)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Nov 27, 2020
1 parent bbaee14 commit e15395f
Show file tree
Hide file tree
Showing 27 changed files with 575 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static partial class SchemaRequestExecutorBuilderExtensions

return builder.ConfigureSchema(b => b.ModifyOptions(configure));
}

public static IRequestExecutorBuilder SetContextData(
this IRequestExecutorBuilder builder,
string key,
Expand All @@ -60,5 +60,23 @@ public static partial class SchemaRequestExecutorBuilderExtensions

return builder.ConfigureSchema(b => b.SetContextData(key, value));
}

/// <summary>
/// Configures the schema to remove types that cannot be reached by the execution engine.
/// </summary>
/// <param name="builder">
/// The <see cref="IRequestExecutorBuilder"/>.
/// </param>
/// <param name="trim">
/// A boolean defining if type trimming shall be applied.
/// </param>
/// <returns>
/// Returns <see cref="IRequestExecutorBuilder"/> so that configurations can be chained.
/// </returns>
public static IRequestExecutorBuilder TrimTypes(
this IRequestExecutorBuilder builder,
bool trim = true) =>
builder.ModifyOptions(o => o.RemoveUnreachableTypes = trim);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ private FieldDelegate CreateFieldMiddleware(IObjectField field, FieldNode select
DirectiveNode directive = selection.Directives[i];
if (_schema.TryGetDirectiveType(directive.Name.Value,
out DirectiveType? directiveType)
&& directiveType.IsExecutable)
&& directiveType.HasMiddleware)
{
yield return Directive.FromDescription(
directiveType,
Expand Down Expand Up @@ -711,7 +711,7 @@ private void OptimizeSelectionSet(CompilerContext context)

for (var i = directives.Count - 1; i >= 0; i--)
{
if (directives[i] is { IsExecutable: true } directive)
if (directives[i] is { Type: { HasMiddleware: true } } directive)
{
next = BuildComponent(directive, next);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
using HotChocolate.Types;

#nullable enable

namespace HotChocolate.Configuration
{
/// <summary>
/// Represents read-only schema options.
/// </summary>
public interface IReadOnlySchemaOptions
{
string QueryTypeName { get; }
/// <summary>
/// Gets the name of the query type.
/// </summary>
string? QueryTypeName { get; }

string MutationTypeName { get; }
/// <summary>
/// Gets or sets the name of the mutation type.
/// </summary>
string? MutationTypeName { get; }

string SubscriptionTypeName { get; }
/// <summary>
/// Gets or sets the name of the subscription type.
/// </summary>
string? SubscriptionTypeName { get; }

/// <summary>
/// Defines if the schema allows the query type to be omitted.
/// </summary>
bool StrictValidation { get; }

/// <summary>
/// Defines if the CSharp XML documentation shall be integrated.
/// </summary>
bool UseXmlDocumentation { get; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,34 @@

namespace HotChocolate.Configuration
{
public interface ISchemaOptions
: IReadOnlySchemaOptions
/// <summary>
/// Represents mutable schema options.
/// </summary>
public interface ISchemaOptions : IReadOnlySchemaOptions
{
/// <summary>
/// Gets or sets the name of the query type.
/// </summary>
new string QueryTypeName { get; set; }

/// <summary>
/// Gets or sets the name of the mutation type.
/// </summary>
new string MutationTypeName { get; set; }

/// <summary>
/// Gets or sets the name of the subscription type.
/// </summary>
new string SubscriptionTypeName { get; set; }

/// <summary>
/// Defines if the schema allows the query type to be omitted.
/// </summary>
new bool StrictValidation { get; set; }

/// <summary>
/// Defines if the CSharp XML documentation shall be integrated.
/// </summary>
new bool UseXmlDocumentation { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@

namespace HotChocolate.Configuration
{
public class ReadOnlySchemaOptions
: IReadOnlySchemaOptions
/// <summary>
/// Represents read-only schema options.
/// </summary>
public class ReadOnlySchemaOptions : IReadOnlySchemaOptions
{
/// <summary>
/// Initializes a new instance of <see cref="ReadOnlySchemaOptions"/>.
/// </summary>
/// <param name="options">
/// The options that shall be wrapped as read-only options.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="options"/> is <c>null</c>.
/// </exception>
public ReadOnlySchemaOptions(IReadOnlySchemaOptions options)
{
if (options is null)
Expand All @@ -24,22 +35,51 @@ public ReadOnlySchemaOptions(IReadOnlySchemaOptions options)
FieldMiddleware = options.FieldMiddleware;
}

/// <summary>
/// Gets the name of the query type.
/// </summary>
public string QueryTypeName { get; }

/// <summary>
/// Gets or sets the name of the mutation type.
/// </summary>
public string MutationTypeName { get; }

/// <summary>
/// Gets or sets the name of the subscription type.
/// </summary>
public string SubscriptionTypeName { get; }

/// <summary>
/// Defines if the schema allows the query type to be omitted.
/// </summary>"
public bool StrictValidation { get; }

/// <summary>
/// Defines if the CSharp XML documentation shall be integrated.
/// </summary>
public bool UseXmlDocumentation { get; }

/// <summary>
/// Defines if fields shall be sorted by name.
/// Default: <c>false</c>
/// </summary>
public bool SortFieldsByName { get; }

/// <summary>
/// Defines if types shall be removed from the schema that are
/// unreachable from the root types.
/// </summary>
public bool RemoveUnreachableTypes { get; }

/// <summary>
/// Defines the default binding behavior.
/// </summary>
public BindingBehavior DefaultBindingBehavior { get; }

/// <summary>
/// Defines on which fields a middleware pipeline can be applied on.
/// </summary>
public FieldMiddlewareApplication FieldMiddleware { get; }
}
}
23 changes: 20 additions & 3 deletions src/HotChocolate/Core/src/Types/Configuration/SchemaOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,34 @@

namespace HotChocolate.Configuration
{
public class SchemaOptions
: ISchemaOptions
/// <summary>
/// Represents mutable schema options.
/// </summary>
public class SchemaOptions : ISchemaOptions
{
/// <summary>
/// Gets or sets the name of the query type.
/// </summary>
public string QueryTypeName { get; set; }

/// <summary>
/// Gets or sets the name of the mutation type.
/// </summary>
public string MutationTypeName { get; set; }

/// <summary>
/// Gets or sets the name of the subscription type.
/// </summary>
public string SubscriptionTypeName { get; set; }

/// <summary>
/// Defines if the schema allows the query type to be omitted.
/// </summary>
public bool StrictValidation { get; set; } = true;

/// <summary>
/// Defines if the CSharp XML documentation shall be integrated.
/// </summary>
public bool UseXmlDocumentation { get; set; } = true;

/// <summary>
Expand Down Expand Up @@ -41,7 +58,7 @@ public class SchemaOptions

public static SchemaOptions FromOptions(IReadOnlySchemaOptions options)
{
return new SchemaOptions
return new()
{
QueryTypeName = options.QueryTypeName,
MutationTypeName = options.MutationTypeName,
Expand Down
57 changes: 44 additions & 13 deletions src/HotChocolate/Core/src/Types/Configuration/TypeTrimmer.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HotChocolate.Types;
Expand All @@ -8,18 +9,47 @@ namespace HotChocolate.Configuration
{
internal sealed class TypeTrimmer
{
private readonly HashSet<TypeSystemObjectBase> _touched =
new HashSet<TypeSystemObjectBase>();
private readonly TypeRegistry _discoveredTypes;
private readonly HashSet<TypeSystemObjectBase> _touched = new();
private readonly List<ObjectType> _rootTypes = new();
private readonly List<TypeSystemObjectBase> _discoveredTypes;

public TypeTrimmer(TypeRegistry discoveredTypes)
public TypeTrimmer(IEnumerable<TypeSystemObjectBase> discoveredTypes)
{
_discoveredTypes = discoveredTypes;
if (discoveredTypes is null)
{
throw new ArgumentNullException(nameof(discoveredTypes));
}

_discoveredTypes = discoveredTypes.ToList();
}

public IReadOnlyCollection<TypeSystemObjectBase> Types => _touched;
public void AddOperationType(ObjectType? operationType)
{
if (operationType is not null)
{
_rootTypes.Add(operationType);
}
}

public void VisitRoot(ObjectType rootType)
public IReadOnlyCollection<TypeSystemObjectBase> Trim()
{
foreach (var directiveType in _discoveredTypes.OfType<DirectiveType>())
{
if (directiveType.IsExecutableDirective)
{
_touched.Add(directiveType);
}
}

foreach (ObjectType rootType in _rootTypes)
{
VisitRoot(rootType);
}

return _touched;
}

private void VisitRoot(ObjectType rootType)
{
Visit(rootType);
}
Expand Down Expand Up @@ -63,6 +93,7 @@ private void Visit(TypeSystemObjectBase type)

private void VisitScalar(ScalarType type)
{
VisitDirectives(type);
}

private void VisitEnum(EnumType type)
Expand All @@ -81,7 +112,7 @@ private void VisitObject(ObjectType type)

foreach (InterfaceType interfaceType in type.Interfaces)
{
VisitInterface(interfaceType, true);
VisitInterface(interfaceType);
}

foreach (ObjectField field in type.Fields)
Expand All @@ -107,7 +138,7 @@ private void VisitUnion(UnionType type)
}
}

private void VisitInterface(InterfaceType type, bool implements = false)
private void VisitInterface(InterfaceType type)
{
VisitDirectives(type);

Expand All @@ -123,12 +154,12 @@ private void VisitInterface(InterfaceType type, bool implements = false)
}
}

foreach (ObjectType objectType in
_discoveredTypes.Types.Select(t => t.Type).OfType<ObjectType>())
foreach (IComplexOutputType complexType in
_discoveredTypes.OfType<IComplexOutputType>())
{
if (objectType.IsImplementing(type))
if (complexType.IsImplementing(type))
{
Visit(objectType);
Visit((TypeSystemObjectBase)complexType);
}
}
}
Expand Down
23 changes: 5 additions & 18 deletions src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,24 +412,11 @@ public static Schema Create(SchemaBuilder builder)
{
if (builder._options.RemoveUnreachableTypes)
{
var trimmer = new TypeTrimmer(typeRegistry);

if (definition.QueryType is { })
{
trimmer.VisitRoot(definition.QueryType);
}

if (definition.MutationType is { })
{
trimmer.VisitRoot(definition.MutationType);
}

if (definition.SubscriptionType is { })
{
trimmer.VisitRoot(definition.SubscriptionType);
}

return trimmer.Types;
var trimmer = new TypeTrimmer(typeRegistry.Types.Select(t => t.Type));
trimmer.AddOperationType(definition.QueryType);
trimmer.AddOperationType(definition.MutationType);
trimmer.AddOperationType(definition.SubscriptionType);
return trimmer.Trim();
}

return typeRegistry.Types.Select(t => t.Type).ToList();
Expand Down
2 changes: 0 additions & 2 deletions src/HotChocolate/Core/src/Types/Types/Contracts/IDirective.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public interface IDirective

IReadOnlyList<DirectiveMiddleware> MiddlewareComponents { get; }

bool IsExecutable { get; }

T ToObject<T>();

DirectiveNode ToNode();
Expand Down

0 comments on commit e15395f

Please sign in to comment.