Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -5,6 +5,11 @@ namespace HotChocolate.Fusion.Options;
/// </summary>
public sealed class SatisfiabilityOptions
{
/// <summary>
/// Gets a value indicating whether to show paths in satisfiability error messages.
/// </summary>
public bool IncludeSatisfiabilityPaths { get; set; }

/// <summary>
/// A collection of fields that should be ignored during the satisfiability analysis
/// because they are known to be non-accessible. The key is the qualified field name (e.g.
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 @@ -408,6 +408,9 @@
<data name="RequirementsValidator_NoOtherSchemasContainField" xml:space="preserve">
<value>No other schemas contain the field '{0}.{1}'.</value>
</data>
<data name="RequirementsValidator_UnableToAccessField" xml:space="preserve">
<value>Unable to access the required field '{0}.{1}'.</value>
</data>
<data name="RequirementsValidator_UnableToAccessFieldOnPath" xml:space="preserve">
<value>Unable to access the required field '{0}.{1}' on path '{2}'.</value>
</data>
Expand Down Expand Up @@ -435,6 +438,9 @@
<data name="SatisfiabilityValidator_NoLookupsFoundForType" xml:space="preserve">
<value>No lookups found for type '{0}' in schema '{1}'.</value>
</data>
<data name="SatisfiabilityValidator_UnableToAccessField" xml:space="preserve">
<value>Unable to access the field '{0}.{1}'.</value>
</data>
<data name="SatisfiabilityValidator_UnableToAccessFieldOnPath" xml:space="preserve">
<value>Unable to access the field '{0}.{1}' on path '{2}'.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

namespace HotChocolate.Fusion.Satisfiability;

internal sealed class RequirementsValidator(MutableSchemaDefinition schema)
internal sealed class RequirementsValidator(
MutableSchemaDefinition schema,
bool includeSatisfiabilityPaths = false)
{
public ImmutableArray<SatisfiabilityError> Validate(
SelectionSetNode requirements,
Expand Down Expand Up @@ -62,14 +64,19 @@ private ImmutableArray<SatisfiabilityError> Visit(
if (fieldErrors.Length != 0)
{
var type = context.TypeContext.Peek();

errors.Add(new SatisfiabilityError(
string.Format(
RequirementsValidator_UnableToAccessFieldOnPath,
type.Name,
fieldNode.Name.Value,
context.Path),
[.. fieldErrors]));
var message =
includeSatisfiabilityPaths
? string.Format(
RequirementsValidator_UnableToAccessFieldOnPath,
type.Name,
fieldNode.Name.Value,
context.Path)
: string.Format(
RequirementsValidator_UnableToAccessField,
type.Name,
fieldNode.Name.Value);

errors.Add(new SatisfiabilityError(message, [.. fieldErrors]));
}

break;
Expand Down Expand Up @@ -191,7 +198,7 @@ private ImmutableArray<SatisfiabilityError> Visit(
if (requirements is not null)
{
var requirementErrors =
new RequirementsValidator(schema).Validate(
new RequirementsValidator(schema, includeSatisfiabilityPaths).Validate(
requirements,
type,
context.Path.Peek(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,30 @@

namespace HotChocolate.Fusion;

internal sealed class SatisfiabilityValidator(
MutableSchemaDefinition schema,
ICompositionLog log,
SatisfiabilityOptions? options = null)
internal sealed class SatisfiabilityValidator
{
private readonly SatisfiabilityOptions _options = options ?? new SatisfiabilityOptions();
private readonly RequirementsValidator _requirementsValidator = new(schema);
private readonly SatisfiabilityOptions _options;
private readonly RequirementsValidator _requirementsValidator;
private readonly MutableSchemaDefinition _schema;
private readonly ICompositionLog _log;

public SatisfiabilityValidator(
MutableSchemaDefinition schema,
ICompositionLog log,
SatisfiabilityOptions? options = null)
{
_schema = schema;
_log = log;
_options = options ?? new SatisfiabilityOptions();
_requirementsValidator = new RequirementsValidator(schema, _options.IncludeSatisfiabilityPaths);
}

public CompositionResult Validate()
{
var context = new SatisfiabilityValidatorContext();

MutableObjectTypeDefinition?[] rootTypes =
[schema.QueryType, schema.MutationType, schema.SubscriptionType];
[_schema.QueryType, _schema.MutationType, _schema.SubscriptionType];

foreach (var rootType in rootTypes)
{
Expand All @@ -39,7 +49,7 @@ public CompositionResult Validate()
}
}

return log.HasErrors
return _log.HasErrors
? ErrorHelper.SatisfiabilityValidationFailed()
: CompositionResult.Success();
}
Expand All @@ -60,8 +70,8 @@ private void VisitObjectType(
// The node and nodes fields are "virtual" fields that might not directly map
// to an underlying source schema, so we have to validate them differently.
if (field.Name is FieldNames.Node or FieldNames.Nodes
&& objectType == schema.QueryType
&& schema.Types.TryGetType<IInterfaceTypeDefinition>(WellKnownTypeNames.Node, out var nodeType)
&& objectType == _schema.QueryType
&& _schema.Types.TryGetType<IInterfaceTypeDefinition>(WellKnownTypeNames.Node, out var nodeType)
&& field.Type.NamedType() == nodeType)
{
if (field.Name == FieldNames.Nodes)
Expand Down Expand Up @@ -107,7 +117,7 @@ private void VisitOutputField(
// If the field is marked as partial, it must be provided by the current schema for it
// to be an option.
if (field.IsPartial(schemaName)
&& previousPathItem?.Provides(field, type, schemaName, schema) != true)
&& previousPathItem?.Provides(field, type, schemaName, _schema) != true)
{
continue;
}
Expand Down Expand Up @@ -178,7 +188,7 @@ private void VisitOutputField(
// Visit each of the possible types that the field may return.
if (fieldType is MutableComplexTypeDefinition or MutableUnionTypeDefinition)
{
var possibleTypes = fieldType.GetPossibleTypes(schemaName, schema);
var possibleTypes = fieldType.GetPossibleTypes(schemaName, _schema);

foreach (var possibleType in possibleTypes)
{
Expand Down Expand Up @@ -211,15 +221,21 @@ private void VisitOutputField(
return;
}

var error = new SatisfiabilityError(
string.Format(
SatisfiabilityValidator_UnableToAccessFieldOnPath,
type.Name,
field.Name,
context.Path),
[.. errors]);
var message =
_options.IncludeSatisfiabilityPaths
? string.Format(
SatisfiabilityValidator_UnableToAccessFieldOnPath,
type.Name,
field.Name,
context.Path)
: string.Format(
SatisfiabilityValidator_UnableToAccessField,
type.Name,
field.Name);

var error = new SatisfiabilityError(message, [.. errors]);

log.Write(
_log.Write(
LogEntryBuilder.New()
.SetMessage(error.ToString())
.SetCode(LogEntryCodes.Unsatisfiable)
Expand All @@ -235,9 +251,9 @@ private void VisitNodeField(
IInterfaceTypeDefinition nodeType,
SatisfiabilityValidatorContext context)
{
foreach (var possibleType in schema.GetPossibleTypes(nodeType))
foreach (var possibleType in _schema.GetPossibleTypes(nodeType))
{
var byIdLookups = schema
var byIdLookups = _schema
.GetPossibleFusionLookupDirectivesById(possibleType);

var hasNodeLookup = false;
Expand All @@ -252,7 +268,7 @@ private void VisitNodeField(
var lookupFieldDefinition = ParseFieldDefinition(fieldDirectiveArgument);
var lookupFieldTypeName = lookupFieldDefinition.Type.NamedType().Name.Value;

if (!schema.Types.TryGetType(lookupFieldTypeName, out var lookupFieldNamedType))
if (!_schema.Types.TryGetType(lookupFieldTypeName, out var lookupFieldNamedType))
{
continue;
}
Expand All @@ -279,7 +295,7 @@ private void VisitNodeField(
var error = new SatisfiabilityError(
string.Format(SatisfiabilityValidator_NodeTypeHasNoNodeLookup, possibleType.Name));

log.Write(
_log.Write(
LogEntryBuilder.New()
.SetMessage(error.ToString())
.SetCode(LogEntryCodes.Unsatisfiable)
Expand All @@ -300,7 +316,7 @@ private ImmutableArray<SatisfiabilityError> ValidateSourceSchemaTransition(
var errors = new List<SatisfiabilityError>();

var lookupDirectives =
schema.GetPossibleFusionLookupDirectives(type, transitionToSchemaName);
_schema.GetPossibleFusionLookupDirectives(type, transitionToSchemaName);

if (!lookupDirectives.Any() && !CanTransitionToSchemaThroughPath(context.Path, transitionToSchemaName))
{
Expand Down Expand Up @@ -365,7 +381,7 @@ private bool CanTransitionToSchemaThroughPath(
foreach (var pathItem in path)
{
var lookupDirectives =
schema.GetPossibleFusionLookupDirectives(
_schema.GetPossibleFusionLookupDirectives(
pathItem.Type,
schemaName);

Expand Down
Loading
Loading