diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Options/SatisfiabilityOptions.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Options/SatisfiabilityOptions.cs index d32383b690b..03ee555fef6 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Options/SatisfiabilityOptions.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Options/SatisfiabilityOptions.cs @@ -5,6 +5,11 @@ namespace HotChocolate.Fusion.Options; /// public sealed class SatisfiabilityOptions { + /// + /// Gets a value indicating whether to show paths in satisfiability error messages. + /// + public bool IncludeSatisfiabilityPaths { get; set; } + /// /// 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. diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.Designer.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.Designer.cs index 2615f4805b0..6c32735fb05 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.Designer.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.Designer.cs @@ -1238,6 +1238,15 @@ internal static string RequirementsValidator_NoOtherSchemasContainField { } } + /// + /// Looks up a localized string similar to Unable to access the required field '{0}.{1}'.. + /// + internal static string RequirementsValidator_UnableToAccessField { + get { + return ResourceManager.GetString("RequirementsValidator_UnableToAccessField", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unable to access the required field '{0}.{1}' on path '{2}'.. /// @@ -1319,6 +1328,15 @@ internal static string SatisfiabilityValidator_NoLookupsFoundForType { } } + /// + /// Looks up a localized string similar to Unable to access the field '{0}.{1}'.. + /// + internal static string SatisfiabilityValidator_UnableToAccessField { + get { + return ResourceManager.GetString("SatisfiabilityValidator_UnableToAccessField", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unable to access the field '{0}.{1}' on path '{2}'.. /// diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.resx b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.resx index 4f443e50546..73f3d0ddfdf 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.resx +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Properties/CompositionResources.resx @@ -408,6 +408,9 @@ No other schemas contain the field '{0}.{1}'. + + Unable to access the required field '{0}.{1}'. + Unable to access the required field '{0}.{1}' on path '{2}'. @@ -435,6 +438,9 @@ No lookups found for type '{0}' in schema '{1}'. + + Unable to access the field '{0}.{1}'. + Unable to access the field '{0}.{1}' on path '{2}'. diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Satisfiability/RequirementsValidator.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Satisfiability/RequirementsValidator.cs index 4324be05a5b..653588e52b5 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Satisfiability/RequirementsValidator.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Satisfiability/RequirementsValidator.cs @@ -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 Validate( SelectionSetNode requirements, @@ -62,14 +64,19 @@ private ImmutableArray 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; @@ -191,7 +198,7 @@ private ImmutableArray Visit( if (requirements is not null) { var requirementErrors = - new RequirementsValidator(schema).Validate( + new RequirementsValidator(schema, includeSatisfiabilityPaths).Validate( requirements, type, context.Path.Peek(), diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/SatisfiabilityValidator.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/SatisfiabilityValidator.cs index e40bc0b90e5..8a71abadb20 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/SatisfiabilityValidator.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/SatisfiabilityValidator.cs @@ -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) { @@ -39,7 +49,7 @@ public CompositionResult Validate() } } - return log.HasErrors + return _log.HasErrors ? ErrorHelper.SatisfiabilityValidationFailed() : CompositionResult.Success(); } @@ -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(WellKnownTypeNames.Node, out var nodeType) + && objectType == _schema.QueryType + && _schema.Types.TryGetType(WellKnownTypeNames.Node, out var nodeType) && field.Type.NamedType() == nodeType) { if (field.Name == FieldNames.Nodes) @@ -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; } @@ -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) { @@ -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) @@ -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; @@ -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; } @@ -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) @@ -300,7 +316,7 @@ private ImmutableArray ValidateSourceSchemaTransition( var errors = new List(); var lookupDirectives = - schema.GetPossibleFusionLookupDirectives(type, transitionToSchemaName); + _schema.GetPossibleFusionLookupDirectives(type, transitionToSchemaName); if (!lookupDirectives.Any() && !CanTransitionToSchemaThroughPath(context.Path, transitionToSchemaName)) { @@ -365,7 +381,7 @@ private bool CanTransitionToSchemaThroughPath( foreach (var pathItem in path) { var lookupDirectives = - schema.GetPossibleFusionLookupDirectives( + _schema.GetPossibleFusionLookupDirectives( pathItem.Type, schemaName); diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Composition.Tests/SatisfiabilityValidatorTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Composition.Tests/SatisfiabilityValidatorTests.cs index 0d8c4566bc2..cc86ba2bbb0 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Composition.Tests/SatisfiabilityValidatorTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Composition.Tests/SatisfiabilityValidatorTests.cs @@ -53,7 +53,8 @@ type User { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -109,7 +110,8 @@ type Product { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -168,7 +170,8 @@ type Product { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -307,7 +310,8 @@ type Address @shareable { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -362,7 +366,8 @@ type Category @key(fields: "id") { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -1456,7 +1461,8 @@ type Product { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -1617,7 +1623,8 @@ interface Animal { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -1964,7 +1971,8 @@ type Dog { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -2015,7 +2023,8 @@ type Product { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -2102,7 +2111,8 @@ type Section { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -2281,7 +2291,8 @@ type Viewer { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -2340,7 +2351,8 @@ type Viewer { var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); @@ -2506,7 +2518,8 @@ type Section { "Section.name", ["A:Query.productById -> B:Product.section
"] } - } + }, + IncludeSatisfiabilityPaths = true }; var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); @@ -2534,7 +2547,8 @@ public void GlobalObjectIdentification_Examples(string[] sdl, bool success, stri var schema = merger.Merge().Value; var log = new CompositionLog(); - var satisfiabilityValidator = new SatisfiabilityValidator(schema, log); + var options = new SatisfiabilityOptions { IncludeSatisfiabilityPaths = true }; + var satisfiabilityValidator = new SatisfiabilityValidator(schema, log, options); // act var result = satisfiabilityValidator.Validate(); diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionComposeCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionComposeCommand.cs index 69a1a4771e8..859db16bcac 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionComposeCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionComposeCommand.cs @@ -67,6 +67,11 @@ public FusionComposeCommand() : base("compose") Description = ComposeCommand_EnableGlobalObjectIdentification_Description }; + var includeSatisfiabilityPathsOption = new Option("--include-satisfiability-paths") + { + Description = ComposeCommand_IncludeSatisfiabilityPaths_Description + }; + var watchModeOption = new Option("--watch") { Arity = ArgumentArity.ZeroOrOne }; var printSchemaOption = new Option("--print") { IsHidden = true }; @@ -76,6 +81,7 @@ public FusionComposeCommand() : base("compose") AddOption(archiveOption); AddOption(environmentOption); AddOption(enableGlobalIdsOption); + AddOption(includeSatisfiabilityPathsOption); AddOption(watchModeOption); AddOption(printSchemaOption); @@ -86,6 +92,7 @@ public FusionComposeCommand() : base("compose") var archive = context.ParseResult.GetValueForOption(archiveOption); var environment = context.ParseResult.GetValueForOption(environmentOption); var enableGlobalIds = context.ParseResult.GetValueForOption(enableGlobalIdsOption); + var includeSatisfiabilityPaths = context.ParseResult.GetValueForOption(includeSatisfiabilityPathsOption); var watchMode = context.ParseResult.GetValueForOption(watchModeOption); var printSchema = context.ParseResult.GetValueForOption(printSchemaOption); @@ -96,6 +103,7 @@ public FusionComposeCommand() : base("compose") archive, environment, enableGlobalIds, + includeSatisfiabilityPaths, watchMode, printSchema, context.GetCancellationToken()); @@ -109,6 +117,7 @@ private static async Task ExecuteAsync( string? archiveFile, string? environment, bool? enableGlobalObjectIdentification, + bool? includeSatisfiabilityPaths, bool watchMode, bool printSchema, CancellationToken cancellationToken) @@ -153,6 +162,7 @@ private static async Task ExecuteAsync( archiveFile, environment, enableGlobalObjectIdentification, + includeSatisfiabilityPaths, cancellationToken); } @@ -166,6 +176,10 @@ private static async Task ExecuteAsync( Merger = new CompositionSettings.MergerSettings { EnableGlobalObjectIdentification = enableGlobalObjectIdentification + }, + Satisfiability = new CompositionSettings.SatisfiabilitySettings + { + IncludeSatisfiabilityPaths = includeSatisfiabilityPaths } }, printSchema, @@ -179,6 +193,7 @@ private static async Task WatchComposeAsync( string archiveFile, string? environment, bool? enableGlobalObjectIdentification, + bool? includeSatisfiabilityPaths, CancellationToken cancellationToken) { console.Out.WriteLine("🔍 Starting watch mode..."); @@ -194,6 +209,10 @@ await ComposeAsync( Merger = new CompositionSettings.MergerSettings { EnableGlobalObjectIdentification = enableGlobalObjectIdentification + }, + Satisfiability = new CompositionSettings.SatisfiabilitySettings + { + IncludeSatisfiabilityPaths = includeSatisfiabilityPaths } }, false, @@ -218,6 +237,7 @@ await ComposeAsync( archiveFile, environment, enableGlobalObjectIdentification, + includeSatisfiabilityPaths, cancellationToken); var sourceSchemaFileWatchers = new List(); @@ -334,6 +354,7 @@ private static async Task ProcessCompositionRequestsAsync( string archiveFile, string? environment, bool? enableGlobalObjectIdentification, + bool? includeSatisfiabilityPaths, CancellationToken cancellationToken) { var lastComposition = DateTime.MinValue; @@ -369,6 +390,10 @@ await ComposeAsync( Merger = new CompositionSettings.MergerSettings { EnableGlobalObjectIdentification = enableGlobalObjectIdentification + }, + Satisfiability = new CompositionSettings.SatisfiabilitySettings + { + IncludeSatisfiabilityPaths = includeSatisfiabilityPaths } }, false, @@ -529,8 +554,8 @@ await archive.GetSourceSchemaNamesAsync(cancellationToken), compositionSettings?.MergeInto(existingCompositionSettings) ?? existingCompositionSettings; var sourceSchemaOptionsMap = new Dictionary(); - var mergerOptions = mergedCompositionSettings.Merger?.ToOptions() ?? new SourceSchemaMergerOptions(); - var satisfiabilityOptions = new SatisfiabilityOptions(); + var mergerOptions = mergedCompositionSettings.Merger.ToOptions(); + var satisfiabilityOptions = mergedCompositionSettings.Satisfiability.ToOptions(); foreach (var (sourceSchemaName, (_, sourceSchemaSettings)) in sourceSchemas) { @@ -551,7 +576,7 @@ await archive.GetSourceSchemaNamesAsync(cancellationToken), Satisfiability = satisfiabilityOptions }; - if (existingCompositionSettings.Merger?.EnableGlobalObjectIdentification + if (existingCompositionSettings.Merger.EnableGlobalObjectIdentification != schemaComposerOptions.Merger.EnableGlobalObjectIdentification) { compositionLog.Write( @@ -746,6 +771,10 @@ private static async Task SaveCompositionSettingsAsync( Merger = new CompositionSettings.MergerSettings { EnableGlobalObjectIdentification = options.Merger.EnableGlobalObjectIdentification + }, + Satisfiability = new CompositionSettings.SatisfiabilitySettings + { + IncludeSatisfiabilityPaths = options.Satisfiability.IncludeSatisfiabilityPaths } }; var settingsJson = JsonSerializer.SerializeToDocument( diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionSettingsSetCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionSettingsSetCommand.cs index f9397d603b2..295d82755ae 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionSettingsSetCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionSettingsSetCommand.cs @@ -69,7 +69,7 @@ private static async Task ExecuteAsync( var tags = settingValue .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - compositionSettings.Preprocessor!.ExcludeByTag = tags.ToHashSet(); + compositionSettings.Preprocessor.ExcludeByTag = tags.ToHashSet(); break; case SettingNames.GlobalObjectIdentification: @@ -79,7 +79,17 @@ private static async Task ExecuteAsync( return ExitCodes.Error; } - compositionSettings.Merger!.EnableGlobalObjectIdentification = enableGlobalObjectIdentification; + compositionSettings.Merger.EnableGlobalObjectIdentification = enableGlobalObjectIdentification; + break; + + case SettingNames.IncludeSatisfiabilityPaths: + if (!bool.TryParse(settingValue, out var includeSatisfiabilityPaths)) + { + console.ErrorLine($"Expected a boolean value for setting '{settingName}'."); + return ExitCodes.Error; + } + + compositionSettings.Satisfiability.IncludeSatisfiabilityPaths = includeSatisfiabilityPaths; break; default: @@ -104,5 +114,6 @@ private static class SettingNames { public const string ExcludeByTag = "exclude-by-tag"; public const string GlobalObjectIdentification = "global-object-identification"; + public const string IncludeSatisfiabilityPaths = "include-satisfiability-paths"; } } diff --git a/src/Nitro/CommandLine/src/CommandLine/Extensions/SettingsExtensions.cs b/src/Nitro/CommandLine/src/CommandLine/Extensions/SettingsExtensions.cs index d2ed97cc7e9..b5834987c6c 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Extensions/SettingsExtensions.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Extensions/SettingsExtensions.cs @@ -14,20 +14,26 @@ public CompositionSettings MergeInto(CompositionSettings settings) Merger = new CompositionSettings.MergerSettings { AddFusionDefinitions = - compositionSettings.Merger?.AddFusionDefinitions - ?? settings.Merger?.AddFusionDefinitions, + compositionSettings.Merger.AddFusionDefinitions + ?? settings.Merger.AddFusionDefinitions, CacheControlMergeBehavior = - compositionSettings.Merger?.CacheControlMergeBehavior - ?? settings.Merger?.CacheControlMergeBehavior, + compositionSettings.Merger.CacheControlMergeBehavior + ?? settings.Merger.CacheControlMergeBehavior, EnableGlobalObjectIdentification = - compositionSettings.Merger?.EnableGlobalObjectIdentification - ?? settings.Merger?.EnableGlobalObjectIdentification, + compositionSettings.Merger.EnableGlobalObjectIdentification + ?? settings.Merger.EnableGlobalObjectIdentification, RemoveUnreferencedDefinitions = - compositionSettings.Merger?.RemoveUnreferencedDefinitions - ?? settings.Merger?.RemoveUnreferencedDefinitions, + compositionSettings.Merger.RemoveUnreferencedDefinitions + ?? settings.Merger.RemoveUnreferencedDefinitions, TagMergeBehavior = - compositionSettings.Merger?.TagMergeBehavior - ?? settings.Merger?.TagMergeBehavior + compositionSettings.Merger.TagMergeBehavior + ?? settings.Merger.TagMergeBehavior + }, + Satisfiability = new CompositionSettings.SatisfiabilitySettings + { + IncludeSatisfiabilityPaths = + compositionSettings.Satisfiability.IncludeSatisfiabilityPaths + ?? settings.Satisfiability.IncludeSatisfiabilityPaths } }; } @@ -79,6 +85,21 @@ public SourceSchemaMergerOptions ToOptions() } } + extension(CompositionSettings.SatisfiabilitySettings satisfiabilitySettings) + { + public SatisfiabilityOptions ToOptions() + { + var satisfiabilityOptions = new SatisfiabilityOptions(); + + if (satisfiabilitySettings.IncludeSatisfiabilityPaths is { } includeSatisfiabilityPaths) + { + satisfiabilityOptions.IncludeSatisfiabilityPaths = includeSatisfiabilityPaths; + } + + return satisfiabilityOptions; + } + } + extension(SourceSchemaSettings sourceSchemaSettings) { public SourceSchemaOptions ToOptions() diff --git a/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.Designer.cs b/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.Designer.cs index ac7f4cafd67..a4a3ece8ffa 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.Designer.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.Designer.cs @@ -176,6 +176,15 @@ internal static string ComposeCommand_GlobalObjectIdentification_Enabled { } } + /// + /// Looks up a localized string similar to Determines whether to include paths in satisfiability error messages.. + /// + internal static string ComposeCommand_IncludeSatisfiabilityPaths_Description { + get { + return ResourceManager.GetString("ComposeCommand_IncludeSatisfiabilityPaths_Description", resourceCulture); + } + } + /// /// Looks up a localized string similar to Specifies the path to a source schema file (.graphqls) to include in the composition.. /// diff --git a/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.resx b/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.resx index 025105d5ea8..9e6fe41e6d9 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.resx +++ b/src/Nitro/CommandLine/src/CommandLine/Properties/CommandLineResources.resx @@ -57,6 +57,9 @@ ❌ Working directory '{0}' does not exist. + + Determines whether to include paths in satisfiability error messages. + Specifies the path to a source schema file (.graphqls) to include in the composition. diff --git a/src/Nitro/CommandLine/src/CommandLine/Settings/CompositionSettings.cs b/src/Nitro/CommandLine/src/CommandLine/Settings/CompositionSettings.cs index 52f9b7c46ce..623d4294297 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Settings/CompositionSettings.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Settings/CompositionSettings.cs @@ -4,9 +4,11 @@ namespace ChilliCream.Nitro.CommandLine.Settings; internal sealed record CompositionSettings { - public PreprocessorSettings? Preprocessor { get; init; } = new(); + public PreprocessorSettings Preprocessor { get; init; } = new(); - public MergerSettings? Merger { get; init; } = new(); + public MergerSettings Merger { get; init; } = new(); + + public SatisfiabilitySettings Satisfiability { get; init; } = new(); internal sealed record PreprocessorSettings { @@ -25,4 +27,9 @@ internal sealed record MergerSettings public DirectiveMergeBehavior? TagMergeBehavior { get; init; } } + + internal sealed record SatisfiabilitySettings + { + public bool? IncludeSatisfiabilityPaths { get; set; } + } } diff --git a/src/Nitro/CommandLine/src/CommandLine/Settings/SettingsJsonSerializerContext.cs b/src/Nitro/CommandLine/src/CommandLine/Settings/SettingsJsonSerializerContext.cs index 819ec35b90b..5a883cb1812 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Settings/SettingsJsonSerializerContext.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Settings/SettingsJsonSerializerContext.cs @@ -5,8 +5,10 @@ namespace ChilliCream.Nitro.CommandLine.Settings; [JsonSerializable(typeof(CompositionSettings))] [JsonSerializable(typeof(CompositionSettings.PreprocessorSettings), TypeInfoPropertyName = "CompositionPreprocessorSettings")] +[JsonSerializable(typeof(CompositionSettings.SatisfiabilitySettings), TypeInfoPropertyName = "CompositionSatisfiabilitySettings")] [JsonSerializable(typeof(SourceSchemaSettings))] [JsonSerializable(typeof(SourceSchemaSettings.PreprocessorSettings), TypeInfoPropertyName = "SourceSchemaPreprocessorSettings")] +[JsonSerializable(typeof(SourceSchemaSettings.SatisfiabilitySettings), TypeInfoPropertyName = "SourceSchemaSatisfiabilitySettings")] [JsonSourceGenerationOptions( Converters = [typeof(JsonStringEnumConverter)], PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] diff --git a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionComposeCommandTests.cs b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionComposeCommandTests.cs index bf8984a21c7..c362e435a6f 100644 --- a/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionComposeCommandTests.cs +++ b/src/Nitro/CommandLine/test/CommandLine.Tests/Commands/Fusion/FusionComposeCommandTests.cs @@ -389,7 +389,8 @@ public async Task Compose_IgnoredNonAccessibleFields() "--source-schema-file", "__resources__/valid-example-2/source-schema-b.graphqls", "--fusion-archive", - archiveFileName + archiveFileName, + "--include-satisfiability-paths" ]; var testConsole = new TestConsole();