Skip to content

Commit

Permalink
[Fusion] Refactored SchemaComposer
Browse files Browse the repository at this point in the history
  • Loading branch information
glen-84 committed Jan 28, 2025
1 parent 0fe4ffb commit ad5785a
Show file tree
Hide file tree
Showing 62 changed files with 697 additions and 414 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace HotChocolate.Fusion;

internal sealed class CompositionContext(
ImmutableArray<SchemaDefinition> schemaDefinitions,
ImmutableSortedSet<SchemaDefinition> schemaDefinitions,
ICompositionLog compositionLog)
{
/// <summary>
/// Gets the schema definitions.
/// </summary>
public ImmutableArray<SchemaDefinition> SchemaDefinitions { get; } = schemaDefinitions;
public ImmutableSortedSet<SchemaDefinition> SchemaDefinitions { get; } = schemaDefinitions;

/// <summary>
/// Gets the composition log.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace HotChocolate.Fusion.Errors;

internal static class ErrorHelper
{
public static CompositionError SourceSchemaParsingFailed()
=> new(ErrorHelper_SourceSchemaParsingFailed);

public static CompositionError SourceSchemaValidationFailed()
=> new(ErrorHelper_SourceSchemaValidationFailed);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public static class LogEntryCodes
public const string InputFieldDefaultMismatch = "INPUT_FIELD_DEFAULT_MISMATCH";
public const string InputFieldTypesNotMergeable = "INPUT_FIELD_TYPES_NOT_MERGEABLE";
public const string InputWithMissingRequiredFields = "INPUT_WITH_MISSING_REQUIRED_FIELDS";
public const string InvalidGraphQL = "INVALID_GRAPHQL";
public const string KeyDirectiveInFieldsArg = "KEY_DIRECTIVE_IN_FIELDS_ARG";
public const string KeyFieldsHasArgs = "KEY_FIELDS_HAS_ARGS";
public const string KeyFieldsSelectInvalidType = "KEY_FIELDS_SELECT_INVALID_TYPE";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ public static LogEntry InputWithMissingRequiredFields(
schema);
}

public static LogEntry InvalidGraphQL(string exceptionMessage)
{
return new LogEntry(
string.Format(LogEntryHelper_InvalidGraphQL, exceptionMessage),
LogEntryCodes.InvalidGraphQL,
severity: LogSeverity.Error);
}

public static LogEntry KeyDirectiveInFieldsArgument(
string entityTypeName,
Directive keyDirective,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

namespace HotChocolate.Fusion;

internal sealed class PostMergeValidator(IEnumerable<object> rules)
#pragma warning disable CS9113 // Parameter is unread.
internal sealed class PostMergeValidator(
SchemaDefinition mergedSchema,
ImmutableArray<object> rules)
#pragma warning restore CS9113 // Parameter is unread.
{
private readonly ImmutableArray<object> _rules = [.. rules];

public CompositionResult Validate(SchemaDefinition _)
public CompositionResult Validate()
{
// FIXME: Implement.
return CompositionResult.Success();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@
using HotChocolate.Fusion.Errors;
using HotChocolate.Fusion.Events;
using HotChocolate.Fusion.Info;
using HotChocolate.Fusion.Logging.Contracts;
using HotChocolate.Fusion.PreMergeValidation;
using HotChocolate.Fusion.Results;
using HotChocolate.Skimmed;

namespace HotChocolate.Fusion;

internal sealed class PreMergeValidator(IEnumerable<object> rules)
internal sealed class PreMergeValidator(
ImmutableSortedSet<SchemaDefinition> schemas,
ImmutableArray<object> rules,
ICompositionLog log)
{
private readonly ImmutableArray<object> _rules = [.. rules];

public CompositionResult Validate(CompositionContext context)
public CompositionResult Validate()
{
PublishEvents(context);
PublishEvents();

return context.Log.HasErrors
return log.HasErrors
? ErrorHelper.PreMergeValidationFailed()
: CompositionResult.Success();
}

private void PublishEvents(CompositionContext context)
private void PublishEvents()
{
var context = new CompositionContext(schemas, log);
MultiValueDictionary<string, TypeInfo> typeGroupByName = [];

foreach (var schema in context.SchemaDefinitions)
Expand Down Expand Up @@ -127,7 +130,7 @@ private void PublishEvents(CompositionContext context)
private void PublishEvent<TEvent>(TEvent @event, CompositionContext context)
where TEvent : IEvent
{
foreach (var rule in _rules)
foreach (var rule in rules)
{
if (rule is IEventHandler<TEvent> handler)
{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
<data name="ErrorHelper_PreMergeValidationFailed" xml:space="preserve">
<value>Pre-merge validation failed. View the composition log for details.</value>
</data>
<data name="ErrorHelper_SourceSchemaParsingFailed" xml:space="preserve">
<value>Source schema parsing failed. View the composition log for details.</value>
</data>
<data name="ErrorHelper_SourceSchemaValidationFailed" xml:space="preserve">
<value>Source schema validation failed. View the composition log for details.</value>
</data>
Expand Down Expand Up @@ -66,6 +69,9 @@
<data name="LogEntryHelper_InputWithMissingRequiredFields" xml:space="preserve">
<value>The input type '{0}' in schema '{1}' must define the required field '{2}'.</value>
</data>
<data name="LogEntryHelper_InvalidGraphQL" xml:space="preserve">
<value>Invalid GraphQL in source schema. Exception message: {0}.</value>
</data>
<data name="LogEntryHelper_KeyDirectiveInFieldsArgument" xml:space="preserve">
<value>A @key directive on type '{0}' in schema '{1}' references field '{2}', which must not include directive applications.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,16 @@ public static implicit operator CompositionResult<TValue>(CompositionResult resu
{
return new CompositionResult<TValue>(result.Errors);
}

public void Deconstruct(
out bool isSuccess,
out bool isFailure,
out TValue value,
out ImmutableArray<CompositionError> errors)
{
isSuccess = IsSuccess;
isFailure = IsFailure;
value = Value;
errors = Errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

namespace HotChocolate.Fusion;

internal sealed class SatisfiabilityValidator
#pragma warning disable CS9113 // Parameter is unread.
internal sealed class SatisfiabilityValidator(SchemaDefinition mergedSchema)
#pragma warning restore CS9113 // Parameter is unread.
{
public CompositionResult Validate(SchemaDefinition _)
public CompositionResult Validate()
{
// FIXME: Implement.
return CompositionResult.Success();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@

namespace HotChocolate.Fusion;

public sealed class SchemaComposer
public sealed class SchemaComposer(IEnumerable<string> sourceSchemas, ICompositionLog log)
{
public CompositionResult<SchemaDefinition> Compose(
IEnumerable<SchemaDefinition> schemaDefinitions,
ICompositionLog compositionLog)
private readonly IEnumerable<string> _sourceSchemas = sourceSchemas
?? throw new ArgumentNullException(nameof(sourceSchemas));

private readonly ICompositionLog _log = log
?? throw new ArgumentNullException(nameof(log));

public CompositionResult<SchemaDefinition> Compose()
{
ArgumentNullException.ThrowIfNull(schemaDefinitions);
ArgumentNullException.ThrowIfNull(compositionLog);
// Parse Source Schemas
var (_, isParseFailure, schemas, parseErrors) =
new SourceSchemaParser(_sourceSchemas, _log).Parse();

var schemas = schemaDefinitions.ToImmutableArray();
var context = new CompositionContext(schemas, compositionLog);
if (isParseFailure)
{
return parseErrors;
}

// Validate Source Schemas
var validationResult =
new SourceSchemaValidator(_sourceSchemaValidationRules).Validate(context);
new SourceSchemaValidator(schemas, s_sourceSchemaValidationRules, _log).Validate();

if (validationResult.IsFailure)
{
Expand All @@ -30,42 +37,43 @@ public CompositionResult<SchemaDefinition> Compose(

// Pre Merge Validation
var preMergeValidationResult =
new PreMergeValidator(_preMergeValidationRules).Validate(context);
new PreMergeValidator(schemas, s_preMergeValidationRules, _log).Validate();

if (preMergeValidationResult.IsFailure)
{
return preMergeValidationResult;
}

// Merge Source Schemas
var mergeResult = new SourceSchemaMerger(schemas).MergeSchemas();
var (_, isMergeFailure, mergedSchema, mergeErrors) =
new SourceSchemaMerger(schemas).Merge();

if (mergeResult.IsFailure)
if (isMergeFailure)
{
return mergeResult;
return mergeErrors;
}

// Post Merge Validation
var postMergeValidationResult =
new PostMergeValidator(_postMergeValidationRules).Validate(mergeResult.Value);
new PostMergeValidator(mergedSchema, s_postMergeValidationRules).Validate();

if (postMergeValidationResult.IsFailure)
{
return postMergeValidationResult;
}

// Validate Satisfiability
var satisfiabilityResult = new SatisfiabilityValidator().Validate(mergeResult.Value);
var satisfiabilityResult = new SatisfiabilityValidator(mergedSchema).Validate();

if (satisfiabilityResult.IsFailure)
{
return satisfiabilityResult;
}

return mergeResult;
return mergedSchema;
}

private static readonly List<object> _sourceSchemaValidationRules =
private static readonly ImmutableArray<object> s_sourceSchemaValidationRules =
[
new DisallowedInaccessibleElementsRule(),
new ExternalOnInterfaceRule(),
Expand Down Expand Up @@ -95,7 +103,7 @@ public CompositionResult<SchemaDefinition> Compose(
new RootSubscriptionUsedRule()
];

private static readonly List<object> _preMergeValidationRules =
private static readonly ImmutableArray<object> s_preMergeValidationRules =
[
new EnumValuesMismatchRule(),
new ExternalArgumentDefaultMismatchRule(),
Expand All @@ -107,5 +115,5 @@ public CompositionResult<SchemaDefinition> Compose(
new OutputFieldTypesMergeableRule()
];

private static readonly List<object> _postMergeValidationRules = [];
private static readonly ImmutableArray<object> s_postMergeValidationRules = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ namespace HotChocolate.Fusion;
internal sealed class SourceSchemaMerger
{
private static readonly RemoveDirectiveNodesSyntaxRewriter RemoveDirectivesRewriter = new();
private readonly ImmutableArray<SchemaDefinition> _schemas;
private readonly ImmutableSortedSet<SchemaDefinition> _schemas;
private readonly FrozenDictionary<SchemaDefinition, string> _schemaConstantNames;
private readonly SourceSchemaMergerOptions _options;
private readonly FrozenDictionary<string, INamedTypeDefinition> _fusionTypeDefinitions;
private readonly FrozenDictionary<string, DirectiveDefinition> _fusionDirectiveDefinitions;

public SourceSchemaMerger(
ImmutableArray<SchemaDefinition> schemas,
ImmutableSortedSet<SchemaDefinition> schemas,
SourceSchemaMergerOptions? options = null)
{
_schemas = schemas;
Expand All @@ -40,7 +40,7 @@ public SourceSchemaMerger(
_fusionDirectiveDefinitions = CreateFusionDirectiveDefinitions();
}

public CompositionResult<SchemaDefinition> MergeSchemas()
public CompositionResult<SchemaDefinition> Merge()
{
var mergedSchema = new SchemaDefinition();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Immutable;
using HotChocolate.Fusion.Comparers;
using HotChocolate.Fusion.Errors;
using HotChocolate.Fusion.Logging;
using HotChocolate.Fusion.Logging.Contracts;
using HotChocolate.Fusion.Results;
using HotChocolate.Skimmed;
using HotChocolate.Skimmed.Serialization;

namespace HotChocolate.Fusion;

internal sealed class SourceSchemaParser(IEnumerable<string> sourceSchemas, ICompositionLog log)
{
public CompositionResult<ImmutableSortedSet<SchemaDefinition>> Parse()
{
var sortedSetBuilder = ImmutableSortedSet.CreateBuilder(new SchemaByNameComparer());

foreach (var sourceSchema in sourceSchemas)
{
try
{
sortedSetBuilder.Add(SchemaParser.Parse(sourceSchema));
}
catch (Exception ex)
{
log.Write(LogEntryHelper.InvalidGraphQL(ex.Message));
break;
}
}

return log.HasErrors
? ErrorHelper.SourceSchemaParsingFailed()
: sortedSetBuilder.ToImmutable();
}
}
Loading

0 comments on commit ad5785a

Please sign in to comment.