diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateDiagnosticEvents.cs index 569f44ef2d0..63dc2f32aa0 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateDiagnosticEvents.cs @@ -21,7 +21,7 @@ public IActivityScope ExecuteRequest(IRequestContext context) { var scopes = new IActivityScope[_listeners.Length]; - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { scopes[i] = _listeners[i].ExecuteRequest(context); } @@ -31,7 +31,7 @@ public IActivityScope ExecuteRequest(IRequestContext context) public void RequestError(IRequestContext context, Exception exception) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].RequestError(context, exception); } @@ -41,7 +41,7 @@ public IActivityScope ParseDocument(IRequestContext context) { var scopes = new IActivityScope[_listeners.Length]; - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { scopes[i] = _listeners[i].ParseDocument(context); } @@ -51,7 +51,7 @@ public IActivityScope ParseDocument(IRequestContext context) public void SyntaxError(IRequestContext context, IError error) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].SyntaxError(context, error); } @@ -61,7 +61,7 @@ public IActivityScope ValidateDocument(IRequestContext context) { var scopes = new IActivityScope[_listeners.Length]; - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { scopes[i] = _listeners[i].ValidateDocument(context); } @@ -71,7 +71,7 @@ public IActivityScope ValidateDocument(IRequestContext context) public void ValidationErrors(IRequestContext context, IReadOnlyList errors) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].ValidationErrors(context, errors); } @@ -86,7 +86,7 @@ public IActivityScope ResolveFieldValue(IMiddlewareContext context) var scopes = new IActivityScope[_resolverListener.Length]; - for (int i = 0; i < _resolverListener.Length; i++) + for (var i = 0; i < _resolverListener.Length; i++) { scopes[i] = _resolverListener[i].ResolveFieldValue(context); } @@ -96,7 +96,7 @@ public IActivityScope ResolveFieldValue(IMiddlewareContext context) public void ResolverError(IMiddlewareContext context, IError error) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].ResolverError(context, error); } @@ -111,7 +111,7 @@ public IActivityScope RunTask(IExecutionTask task) var scopes = new IActivityScope[_resolverListener.Length]; - for (int i = 0; i < _resolverListener.Length; i++) + for (var i = 0; i < _resolverListener.Length; i++) { scopes[i] = _resolverListener[i].RunTask(task); } @@ -121,7 +121,7 @@ public IActivityScope RunTask(IExecutionTask task) public void TaskError(IExecutionTask task, IError error) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].TaskError(task, error); } @@ -129,7 +129,7 @@ public void TaskError(IExecutionTask task, IError error) public void AddedDocumentToCache(IRequestContext context) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].AddedDocumentToCache(context); } @@ -137,7 +137,7 @@ public void AddedDocumentToCache(IRequestContext context) public void RetrievedDocumentFromCache(IRequestContext context) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].RetrievedDocumentFromCache(context); } @@ -145,7 +145,7 @@ public void RetrievedDocumentFromCache(IRequestContext context) public void RetrievedDocumentFromStorage(IRequestContext context) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].RetrievedDocumentFromStorage(context); } @@ -153,7 +153,7 @@ public void RetrievedDocumentFromStorage(IRequestContext context) public void AddedOperationToCache(IRequestContext context) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].AddedDocumentToCache(context); } @@ -161,7 +161,7 @@ public void AddedOperationToCache(IRequestContext context) public void RetrievedOperationFromCache(IRequestContext context) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].RetrievedDocumentFromCache(context); } @@ -169,7 +169,7 @@ public void RetrievedOperationFromCache(IRequestContext context) public void BatchDispatched(IRequestContext context) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].BatchDispatched(context); } @@ -177,7 +177,7 @@ public void BatchDispatched(IRequestContext context) public void ExecutorCreated(string name, IRequestExecutor executor) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].ExecutorCreated(name, executor); } @@ -185,7 +185,7 @@ public void ExecutorCreated(string name, IRequestExecutor executor) public void ExecutorEvicted(string name, IRequestExecutor executor) { - for (int i = 0; i < _listeners.Length; i++) + for (var i = 0; i < _listeners.Length; i++) { _listeners[i].ExecutorEvicted(name, executor); } @@ -205,7 +205,7 @@ public void Dispose() { if (!_disposed) { - for (int i = 0; i < _scopes.Length; i++) + for (var i = 0; i < _scopes.Length; i++) { _scopes[i].Dispose(); } diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs b/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs index c1bca382e95..68b8173aa2d 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs @@ -22,9 +22,8 @@ internal sealed class RequestExecutorResolver , IInternalRequestExecutorResolver , IDisposable { - private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - private readonly ConcurrentDictionary _executors = - new ConcurrentDictionary(); + private readonly SemaphoreSlim _semaphore = new(1, 1); + private readonly ConcurrentDictionary _executors = new(); private readonly IRequestExecutorOptionsMonitor _optionsMonitor; private readonly IServiceProvider _applicationServices; private bool _disposed; diff --git a/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs b/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs index 03e3f6bc0cf..7df886bc942 100644 --- a/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs +++ b/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs @@ -12,8 +12,7 @@ public class BatchScheduler : IBatchScheduler , IBatchDispatcher { - private readonly ConcurrentQueue> _queue = - new ConcurrentQueue>(); + private readonly ConcurrentQueue> _queue = new(); public bool HasTasks => _queue.Count > 0; diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs index 06a86b0769a..34cd0bbe126 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs @@ -1088,5 +1088,53 @@ internal class TypeResources { return ResourceManager.GetString("NodeDescriptor_IdField_MustBePropertyOrMethod", resourceCulture); } } + + internal static string DeprecatedDirectiveType_TypeDescription { + get { + return ResourceManager.GetString("DeprecatedDirectiveType_TypeDescription", resourceCulture); + } + } + + internal static string DeprecatedDirectiveType_ReasonDescription { + get { + return ResourceManager.GetString("DeprecatedDirectiveType_ReasonDescription", resourceCulture); + } + } + + internal static string IncludeDirectiveType_TypeDescription { + get { + return ResourceManager.GetString("IncludeDirectiveType_TypeDescription", resourceCulture); + } + } + + internal static string IncludeDirectiveType_IfDescription { + get { + return ResourceManager.GetString("IncludeDirectiveType_IfDescription", resourceCulture); + } + } + + internal static string SkipDirectiveType_TypeDescription { + get { + return ResourceManager.GetString("SkipDirectiveType_TypeDescription", resourceCulture); + } + } + + internal static string SkipDirectiveType_IfDescription { + get { + return ResourceManager.GetString("SkipDirectiveType_IfDescription", resourceCulture); + } + } + + internal static string SpecifiedByDirectiveType_TypeDescription { + get { + return ResourceManager.GetString("SpecifiedByDirectiveType_TypeDescription", resourceCulture); + } + } + + internal static string SpecifiedByDirectiveType_UrlDescription { + get { + return ResourceManager.GetString("SpecifiedByDirectiveType_UrlDescription", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx index 9b135b7448a..57aae417d26 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx @@ -644,4 +644,28 @@ Type: `{0}` The ID field must be a property or a method. + + The @deprecated directive is used within the type system definition language to indicate deprecated portions of a GraphQL service’s schema,such as deprecated fields on a type or deprecated enum values. + + + Deprecations include a reason for why it is deprecated, which is formatted using Markdown syntax (as specified by CommonMark). + + + Directs the executor to include this field or fragment only when the `if` argument is true. + + + Included when true. + + + Directs the executor to skip this field or fragment when the `if` argument is true. + + + Skipped when true. + + + The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions. + + + The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types. + diff --git a/src/HotChocolate/Core/src/Types/SchemaSerializer.cs b/src/HotChocolate/Core/src/Types/SchemaSerializer.cs index acc0323b704..2fc2674c64b 100644 --- a/src/HotChocolate/Core/src/Types/SchemaSerializer.cs +++ b/src/HotChocolate/Core/src/Types/SchemaSerializer.cs @@ -59,9 +59,9 @@ public static void Serialize(ISchema schema, TextWriter textWriter) .OfType() .ToList(); - if (schema.QueryType != null - || schema.MutationType != null - || schema.SubscriptionType != null) + if (schema.QueryType is not null + || schema.MutationType is not null + || schema.SubscriptionType is not null) { typeDefinitions.Insert(0, SerializeSchemaTypeDefinition(schema)); } @@ -133,21 +133,21 @@ private static bool IsPublicAndNoScalar(INamedType type) { var operations = new List(); - if (schema.QueryType != null) + if (schema.QueryType is not null) { operations.Add(SerializeOperationType( schema.QueryType, OperationType.Query)); } - if (schema.MutationType != null) + if (schema.MutationType is not null) { operations.Add(SerializeOperationType( schema.MutationType, OperationType.Mutation)); } - if (schema.SubscriptionType != null) + if (schema.SubscriptionType is not null) { operations.Add(SerializeOperationType( schema.SubscriptionType, @@ -171,12 +171,10 @@ private static bool IsPublicAndNoScalar(INamedType type) ObjectType type, OperationType operation) { - return new OperationTypeDefinitionNode - ( + return new( null, operation, - SerializeNamedType(type) - ); + SerializeNamedType(type)); } private static ITypeDefinitionNode SerializeNonScalarTypeDefinition( @@ -320,13 +318,15 @@ private static EnumValueDefinitionNode SerializeEnumValue(IEnumValue enumValue) private static ScalarTypeDefinitionNode SerializeScalarType( ScalarType scalarType) { - return new ScalarTypeDefinitionNode - ( + var directives = scalarType.Directives + .Select(SerializeDirective) + .ToList(); + + return new( null, new NameNode(scalarType.Name), SerializeDescription(scalarType.Description), - Array.Empty() - ); + directives); } private static FieldDefinitionNode SerializeObjectField(IOutputField field) @@ -351,18 +351,14 @@ private static FieldDefinitionNode SerializeObjectField(IOutputField field) } private static InputValueDefinitionNode SerializeInputField( - IInputField inputValue) - { - return new InputValueDefinitionNode - ( + IInputField inputValue) => + new( null, new NameNode(inputValue.Name), SerializeDescription(inputValue.Description), SerializeType(inputValue.Type), inputValue.DefaultValue, - inputValue.Directives.Select(SerializeDirective).ToList() - ); - } + inputValue.Directives.Select(SerializeDirective).ToList()); private static ITypeNode SerializeType(IType type) { @@ -384,21 +380,15 @@ private static ITypeNode SerializeType(IType type) throw new NotSupportedException(); } - private static NamedTypeNode SerializeNamedType(INamedType namedType) - { - return new NamedTypeNode(null, new NameNode(namedType.Name)); - } + private static NamedTypeNode SerializeNamedType(INamedType namedType) => + new(null, new NameNode(namedType.Name)); - private static DirectiveNode SerializeDirective(IDirective directiveType) - { - return directiveType.ToNode(true); - } + private static DirectiveNode SerializeDirective(IDirective directiveType) => + directiveType.ToNode(true); - private static StringValueNode SerializeDescription(string description) - { - return string.IsNullOrEmpty(description) + private static StringValueNode SerializeDescription(string description) => + string.IsNullOrEmpty(description) ? null : new StringValueNode(description); - } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ClrTypeDirectiveReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ClrTypeDirectiveReference.cs index cbd96ba8c0d..2ed3c453986 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ClrTypeDirectiveReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ClrTypeDirectiveReference.cs @@ -1,24 +1,16 @@ using System; +#nullable enable + namespace HotChocolate.Types.Descriptors { - public sealed class ClrTypeDirectiveReference - : IDirectiveReference + public sealed class ClrTypeDirectiveReference : IDirectiveReference { - private readonly Type _clrType; - public ClrTypeDirectiveReference(Type clrType) { - _clrType = clrType ?? - throw new ArgumentNullException(nameof(clrType)); + ClrType = clrType ?? throw new ArgumentNullException(nameof(clrType)); } - public Type ClrType - { - get - { - return _clrType; - } - } + public Type ClrType { get; } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Directive.cs b/src/HotChocolate/Core/src/Types/Types/Directive.cs index 0fa91af3efa..96438252457 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directive.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directive.cs @@ -325,7 +325,7 @@ private T CreateCustomDirective() foreach (Argument argument in directiveType.Arguments) { PropertyInfo property = properties[argument.Name].FirstOrDefault(); - var propertyValue = property?.GetValue(directive); + object? propertyValue = property?.GetValue(directive); IValueNode valueNode = argument.Type.ParseValue(propertyValue); arguments.Add(new ArgumentNode(argument.Name, valueNode)); diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirective.cs b/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirective.cs index e78b4f773cd..bd5e2a0bd44 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirective.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirective.cs @@ -17,7 +17,7 @@ public class DeferDirective /// /// Initializes a new instance of /// - public DeferDirective(bool @if, string? label) + public DeferDirective(bool @if, string? label = null) { If = @if; Label = label; diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirectiveType.cs index 51c8fff30bd..b41a767e699 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirectiveType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/DeferDirectiveType.cs @@ -21,22 +21,29 @@ public class DeferDirectiveType IDirectiveTypeDescriptor descriptor) { descriptor - .Name("defer") + .Name(Names.Defer) .Description(TypeResources.DeferDirectiveType_Description) .Location(DirectiveLocation.FragmentSpread) .Location(DirectiveLocation.InlineFragment); descriptor .Argument(t => t.Label) - .Name(WellKnownDirectives.LabelArgument) + .Name(Names.Label) .Description(TypeResources.DeferDirectiveType_Label_Description) .Type(); descriptor .Argument(t => t.If) - .Name(WellKnownDirectives.IfArgument) + .Name(Names.If) .Description(TypeResources.DeferDirectiveType_If_Description) .Type(); } + + public static class Names + { + public const string Defer = "defer"; + public const string Label = "label"; + public const string If = "if"; + } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/DeprecatedDirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/Directives/DeprecatedDirectiveType.cs index b29f4c20856..96efe228958 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/DeprecatedDirectiveType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/DeprecatedDirectiveType.cs @@ -1,3 +1,5 @@ +using HotChocolate.Properties; + namespace HotChocolate.Types { /// @@ -8,31 +10,29 @@ namespace HotChocolate.Types /// Deprecations include a reason for why it is deprecated, /// which is formatted using Markdown syntax (as specified by CommonMark). /// - public sealed class DeprecatedDirectiveType - : DirectiveType + public sealed class DeprecatedDirectiveType : DirectiveType { protected override void Configure( IDirectiveTypeDescriptor descriptor) { - // TODO : resources descriptor - .Name("deprecated") - .Description( - "The @deprecated directive is used within the " + - "type system definition language to indicate " + - "deprecated portions of a GraphQL service’s schema," + - "such as deprecated fields on a type or deprecated " + - "enum values.") + .Name(Names.Deprecated) + .Description(TypeResources.DeprecatedDirectiveType_TypeDescription) .Location(DirectiveLocation.FieldDefinition) - .Location(DirectiveLocation.EnumValue) + .Location(DirectiveLocation.EnumValue); + + descriptor .Argument(t => t.Reason) - .Name("reason") - .Description( - "Deprecations include a reason for why it is deprecated, " + - "which is formatted using Markdown syntax " + - "(as specified by CommonMark).") + .Name(Names.Reason) + .Description(TypeResources.DeprecatedDirectiveType_ReasonDescription) .Type() .DefaultValue(WellKnownDirectives.DeprecationDefaultReason); } + + public static class Names + { + public const string Deprecated = "deprecated"; + public const string Reason = "reason"; + } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/IncludeDirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/Directives/IncludeDirectiveType.cs index c54b769db4c..165439d426a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/IncludeDirectiveType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/IncludeDirectiveType.cs @@ -1,22 +1,22 @@ -namespace HotChocolate.Types +using HotChocolate.Properties; + +namespace HotChocolate.Types { public sealed class IncludeDirectiveType : DirectiveType { protected override void Configure(IDirectiveTypeDescriptor descriptor) { - descriptor.Name(WellKnownDirectives.Include); - - descriptor.Description( - "Directs the executor to include this field or fragment " + - "only when the `if` argument is true."); - - descriptor.Location(DirectiveLocation.Field) + descriptor + .Name(WellKnownDirectives.Include) + .Description(TypeResources.IncludeDirectiveType_TypeDescription) + .Location(DirectiveLocation.Field) .Location(DirectiveLocation.FragmentSpread) .Location(DirectiveLocation.InlineFragment); - descriptor.Argument(WellKnownDirectives.IfArgument) - .Description("Included when true.") + descriptor + .Argument(WellKnownDirectives.IfArgument) + .Description(TypeResources.IncludeDirectiveType_IfDescription) .Type>(); } } diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/SkipDirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/Directives/SkipDirectiveType.cs index a8c29416c8d..b18083d557c 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/SkipDirectiveType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/SkipDirectiveType.cs @@ -1,23 +1,21 @@ -namespace HotChocolate.Types +using HotChocolate.Properties; + +namespace HotChocolate.Types { - public sealed class SkipDirectiveType - : DirectiveType + public sealed class SkipDirectiveType : DirectiveType { protected override void Configure(IDirectiveTypeDescriptor descriptor) { - descriptor.Name(WellKnownDirectives.Skip); - - descriptor.Description( - "Directs the executor to skip this field or " + - "fragment when the `if` argument is true."); - descriptor - .Location(DirectiveLocation.Field - | DirectiveLocation.FragmentSpread - | DirectiveLocation.InlineFragment); + .Name(WellKnownDirectives.Skip) + .Description(TypeResources.SkipDirectiveType_TypeDescription) + .Location(DirectiveLocation.Field) + .Location(DirectiveLocation.FragmentSpread) + .Location(DirectiveLocation.InlineFragment); - descriptor.Argument(WellKnownDirectives.IfArgument) - .Description("Skipped when true.") + descriptor + .Argument(WellKnownDirectives.IfArgument) + .Description(TypeResources.SkipDirectiveType_IfDescription) .Type>(); } } diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/SpecifiedByDirective.cs b/src/HotChocolate/Core/src/Types/Types/Directives/SpecifiedByDirective.cs new file mode 100644 index 00000000000..0626f06bac8 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Directives/SpecifiedByDirective.cs @@ -0,0 +1,14 @@ +#nullable enable + +namespace HotChocolate.Types +{ + public sealed class SpecifiedByDirective + { + public SpecifiedByDirective(string url) + { + Url = url; + } + + public string Url { get; } + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/SpecifiedByDirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/Directives/SpecifiedByDirectiveType.cs new file mode 100644 index 00000000000..8aab2ac9054 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Directives/SpecifiedByDirectiveType.cs @@ -0,0 +1,39 @@ +using HotChocolate.Properties; + +namespace HotChocolate.Types +{ + /// + /// The `@specifiedBy` directive is used within the type system definition language + /// to provide a URL for specifying the behavior of custom + /// scalar definitions. The URL should point to a human-readable specification of + /// the data format, serialization, and coercion rules for the scalar. For example, + /// a GraphQL system providing a `UUID` scalar might link to + /// [RFC 4122](https://tools.ietf.org/html/rfc4122), + /// or some document defining a reasonable subset of that RFC. If a specification + /// URL is present, systems and tools that are aware of it should conform to its + /// described rules. Built-in scalar types should not provide a URL in this way. + /// + public sealed class SpecifiedByDirectiveType : DirectiveType + { + protected override void Configure( + IDirectiveTypeDescriptor descriptor) + { + descriptor + .Name(Names.SpecifiedBy) + .Description(TypeResources.SpecifiedByDirectiveType_TypeDescription) + .Location(DirectiveLocation.Scalar); + + descriptor + .Argument(t => t.Url) + .Name(Names.Url) + .Description(TypeResources.SpecifiedByDirectiveType_UrlDescription) + .Type>(); + } + + public static class Names + { + public const string SpecifiedBy = "specifiedBy"; + public const string Url = "url"; + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/StreamDirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/Directives/StreamDirectiveType.cs index 7609e51ad8e..824cd65ad81 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/StreamDirectiveType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/StreamDirectiveType.cs @@ -15,28 +15,35 @@ namespace HotChocolate.Types public class StreamDirectiveType : DirectiveType { - protected override void Configure( - IDirectiveTypeDescriptor descriptor) + protected override void Configure(IDirectiveTypeDescriptor descriptor) { descriptor - .Name("stream") + .Name(Names.Stream) .Description(TypeResources.StreamDirectiveType_Description) .Location(DirectiveLocation.Field); descriptor - .Argument("label") + .Argument(Names.Label) .Description(TypeResources.StreamDirectiveType_Label_Description) .Type(); descriptor - .Argument("initialCount") + .Argument(Names.InitialCount) .Description(TypeResources.StreamDirectiveType_InitialCount_Description) .Type>(); descriptor - .Argument(WellKnownDirectives.IfArgument) + .Argument(Names.If) .Description(TypeResources.StreamDirectiveType_If_Description) .Type>(); } + + public static class Names + { + public const string Stream = "stream"; + public const string Label = "label"; + public const string InitialCount = "initialCount"; + public const string If = "if"; + } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Introspection/__Type.cs b/src/HotChocolate/Core/src/Types/Types/Introspection/__Type.cs index 2813c5e0e3a..90865c23346 100644 --- a/src/HotChocolate/Core/src/Types/Types/Introspection/__Type.cs +++ b/src/HotChocolate/Core/src/Types/Types/Introspection/__Type.cs @@ -65,6 +65,11 @@ protected override void Configure(IObjectTypeDescriptor descriptor) .Field(Names.OfType) .Type<__Type>() .ResolveWith(t => t.GetOfType(default!)); + + descriptor + .Field(Names.SpecifiedBy) + .Type() + .ResolveWith(t => t.GetSpecifiedBy(default!)); } private class Resolvers @@ -117,6 +122,11 @@ type switch NonNullType nnt => nnt.Type, _ => null }; + + public string? GetSpecifiedBy([Parent] IType type) => + type is ScalarType scalar + ? scalar.SpecifiedBy.ToString() + : null; } public static class Names @@ -131,6 +141,7 @@ public static class Names public const string EnumValues = "enumValues"; public const string InputFields = "inputFields"; public const string OfType = "ofType"; + public const string SpecifiedBy = "specifiedBy"; public const string IncludeDeprecated = "includeDeprecated"; } } diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs index 2251dab5966..d55811bf390 100644 --- a/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs @@ -13,22 +13,21 @@ public sealed class DateTimeType { private const string _utcFormat = "yyyy-MM-ddTHH\\:mm\\:ss.fffZ"; private const string _localFormat = "yyyy-MM-ddTHH\\:mm\\:ss.fffzzz"; + private const string _specifiedBy = "https://www.graphql-scalars.com/date-time"; public DateTimeType() - : base(ScalarNames.DateTime, BindingBehavior.Implicit) + : this(ScalarNames.DateTime, TypeResources.DateTimeType_Description) { - Description = TypeResources.DateTimeType_Description; } - public DateTimeType(NameString name) - : base(name, BindingBehavior.Implicit) - { - } - - public DateTimeType(NameString name, string description) - : base(name, BindingBehavior.Implicit) + public DateTimeType( + NameString name, + string? description = null, + BindingBehavior bindingBehavior = BindingBehavior.Implicit) + : base(name, bindingBehavior) { Description = description; + SpecifiedBy = new Uri(_specifiedBy); } protected override DateTimeOffset ParseLiteral(StringValueNode valueSyntax) @@ -45,7 +44,7 @@ protected override DateTimeOffset ParseLiteral(StringValueNode valueSyntax) protected override StringValueNode ParseValue(DateTimeOffset runtimeValue) { - return new StringValueNode(Serialize(runtimeValue)); + return new(Serialize(runtimeValue)); } public override IValueNode ParseResult(object? resultValue) diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.Initialization.cs index 160822b30ab..46a3deb5af4 100644 --- a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.Initialization.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using HotChocolate.Configuration; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; using HotChocolate.Utilities; @@ -40,6 +41,17 @@ protected ScalarType(NameString name, BindingBehavior bind = BindingBehavior.Exp internal sealed override void Initialize(ITypeDiscoveryContext context) { context.TypeInterceptor.OnBeforeRegisterDependencies(context, null, _contextData); + + if (_specifiedBy is not null) + { + context.RegisterDependency( + new TypeDependency( + context.TypeInspector.GetTypeRef(typeof(SpecifiedByDirectiveType)), + TypeDependencyKind.Completed)); + context.RegisterDependency( + new ClrTypeDirectiveReference(typeof(SpecifiedByDirective))); + } + OnRegisterDependencies(context, _contextData); context.TypeInterceptor.OnAfterRegisterDependencies(context, null, _contextData); base.Initialize(context); @@ -78,8 +90,19 @@ internal sealed override void CompleteType(ITypeCompletionContext context) IDictionary contextData) { _converter = context.Services.GetTypeConverter(); + + DirectiveDefinition[] directiveDefinitions = + _specifiedBy is null + ? Array.Empty() + : new[] + { + new DirectiveDefinition( + new SpecifiedByDirective(_specifiedBy.ToString()), + context.TypeInspector.GetTypeRef(typeof(SpecifiedByDirectiveType))) + }; + Directives = DirectiveCollection.CreateAndComplete( - context, this, Array.Empty()); + context, this, directiveDefinitions); } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.cs index 1b148a95fec..5c3a018c01f 100644 --- a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType.cs @@ -18,6 +18,8 @@ public abstract partial class ScalarType , ILeafType , IHasDirectives { + private Uri? _specifiedBy; + /// /// Gets the type kind. /// @@ -34,6 +36,23 @@ public abstract partial class ScalarType /// public abstract Type RuntimeType { get; } + /// + /// Gets the optional description of this scalar type. + /// + public Uri? SpecifiedBy + { + get => _specifiedBy; + protected set + { + if (IsCompleted) + { + throw new InvalidOperationException( + TypeResources.TypeSystemObject_DescriptionImmutable); + } + _specifiedBy = value; + } + } + public override IReadOnlyDictionary ContextData => _contextData; public IDirectiveCollection Directives { get; private set; } diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/StringType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/StringType.cs index 4ff39d533fa..e3dd306bbe6 100644 --- a/src/HotChocolate/Core/src/Types/Types/Scalars/StringType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Scalars/StringType.cs @@ -47,10 +47,8 @@ protected override string ParseLiteral(StringValueNode valueSyntax) return valueSyntax.Value; } - protected override StringValueNode ParseValue(string runtimeValue) - { - return new StringValueNode(runtimeValue); - } + protected override StringValueNode ParseValue(string runtimeValue) => + new(runtimeValue); public override IValueNode ParseResult(object? resultValue) => ParseValue(resultValue); diff --git a/src/HotChocolate/Core/test/Execution.Tests/IntrospectionTests.cs b/src/HotChocolate/Core/test/Execution.Tests/IntrospectionTests.cs index cdd9af190ca..982603fd572 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/IntrospectionTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/IntrospectionTests.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using ChilliCream.Testing; using HotChocolate.Configuration; @@ -40,6 +41,30 @@ public async Task TypeNameIntrospectionNotOnQuery() result.MatchSnapshot(); } + [Fact] + public async Task Query_Specified_By() + { + // arrange + var query = "{ __type (name: \"DateTime\") { specifiedBy } }"; + + IRequestExecutor executor = + SchemaBuilder.New() + .AddQueryType(d => d + .Name("Query") + .Field("foo") + .Type() + .Resolve(default(DateTime))) + .Create() + .MakeExecutable(); + + // act + IExecutionResult result = await executor.ExecuteAsync(query); + + // assert + Assert.Null(result.Errors); + result.MatchSnapshot(); + } + [Fact] public async Task TypeIntrospectionOnQuery() { diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap index c44227330b5..a1da20fe421 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap @@ -804,6 +804,18 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "specifiedBy", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap index 8f0a6412a60..587ea584fef 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap @@ -804,6 +804,18 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "specifiedBy", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap index 8f0a6412a60..587ea584fef 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap @@ -804,6 +804,18 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "specifiedBy", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.Query_Specified_By.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.Query_Specified_By.snap new file mode 100644 index 00000000000..14024888fec --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.Query_Specified_By.snap @@ -0,0 +1,7 @@ +{ + "data": { + "__type": { + "specifiedBy": "https://www.graphql-scalars.com/date-time" + } + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTime.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTime.snap index d72d0180506..682c1751cef 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTime.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTime.snap @@ -19,8 +19,11 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap index 18d5ae45260..a56b6a527c3 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap @@ -57,6 +57,9 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD @@ -64,4 +67,4 @@ directive @stream("If this argument label has a value other than null, it will b scalar Date "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Core/test/Types.Tests/SchemaFirstTests.cs b/src/HotChocolate/Core/test/Types.Tests/SchemaFirstTests.cs index f077bfd3e92..b94688616f3 100644 --- a/src/HotChocolate/Core/test/Types.Tests/SchemaFirstTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/SchemaFirstTests.cs @@ -27,8 +27,6 @@ public async Task DescriptionsAreCorrectlyRead() c.Use(next => context => next(context)); }); - var foo = schema.GetType<__Type>("__Type"); - // assert IRequestExecutor executor = schema.MakeExecutable(); IExecutionResult result = await executor.ExecuteAsync(query); diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/CodeFirstTests.Change_DefaultBinding_For_DateTime.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/CodeFirstTests.Change_DefaultBinding_For_DateTime.snap index 216055efdac..bfab211730a 100644 --- a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/CodeFirstTests.Change_DefaultBinding_For_DateTime.snap +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/CodeFirstTests.Change_DefaultBinding_For_DateTime.snap @@ -23,6 +23,9 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD @@ -30,4 +33,4 @@ directive @stream("If this argument label has a value other than null, it will b scalar Date "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap index 974df8b27a4..8442bc1fbbf 100644 --- a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap @@ -1029,6 +1029,18 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "specifiedBy", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap index aa85e9508db..70069ffdb04 100644 --- a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap @@ -925,6 +925,18 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "specifiedBy", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeNullable_FooExplicitBarExplicit.snap b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeNullable_FooExplicitBarExplicit.snap index 6d27cc87ef8..a8a9cf06474 100644 --- a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeNullable_FooExplicitBarExplicit.snap +++ b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeNullable_FooExplicitBarExplicit.snap @@ -30,8 +30,11 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffsetNullable_FooExplicitBarExplicit.snap b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffsetNullable_FooExplicitBarExplicit.snap index 3e8a50027a2..178561b9853 100644 --- a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffsetNullable_FooExplicitBarExplicit.snap +++ b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffsetNullable_FooExplicitBarExplicit.snap @@ -30,8 +30,11 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffset_FooExplicitBarExplicit.snap b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffset_FooExplicitBarExplicit.snap index dcf610100d7..616d533276d 100644 --- a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffset_FooExplicitBarExplicit.snap +++ b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetimeOffset_FooExplicitBarExplicit.snap @@ -30,8 +30,11 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetime_FooExplicitBarExplicit.snap b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetime_FooExplicitBarExplicit.snap index 941b3311270..7221ca07ae6 100644 --- a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetime_FooExplicitBarExplicit.snap +++ b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilterDatetime_FooExplicitBarExplicit.snap @@ -30,8 +30,11 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") diff --git a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilter_FooImplicitBarImplicit.snap b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilter_FooImplicitBarImplicit.snap index acbd4091931..c9364245abd 100644 --- a/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilter_FooImplicitBarImplicit.snap +++ b/src/HotChocolate/Filters/test/Types.Filters.Tests/__snapshots__/ArrayFilterInputTypeTests.Create_ArraySimpleFilter_FooImplicitBarImplicit.snap @@ -462,11 +462,14 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "The built-in `Decimal` scalar type." scalar Decimal diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/StringValueNode.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/StringValueNode.cs index 28382d76816..bd10e1baacc 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/StringValueNode.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/StringValueNode.cs @@ -235,17 +235,17 @@ public ReadOnlySpan AsSpan() public StringValueNode WithLocation(Location? location) { - return new StringValueNode(location, Value, Block); + return new(location, Value, Block); } public StringValueNode WithValue(string value) { - return new StringValueNode(Location, value, false); + return new(Location, value, false); } public StringValueNode WithValue(string value, bool block) { - return new StringValueNode(Location, value, block); + return new(Location, value, block); } } } diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/StringSyntaxWriter.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/StringSyntaxWriter.cs index 35dba38ec8a..1daabc2da14 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/StringSyntaxWriter.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/StringSyntaxWriter.cs @@ -2,11 +2,10 @@ namespace HotChocolate.Language.Utilities { - public class StringSyntaxWriter - : ISyntaxWriter + public class StringSyntaxWriter : ISyntaxWriter { - private static StringSyntaxWriterPool _pool = new StringSyntaxWriterPool(); - private readonly StringBuilder _stringBuilder = new StringBuilder(); + private static readonly StringSyntaxWriterPool _pool = new(); + private readonly StringBuilder _stringBuilder = new(); private int _indent; public static StringSyntaxWriter Rent() diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs index ae8859cd7d0..824dcd1b953 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs @@ -3,9 +3,9 @@ namespace HotChocolate.Language.Utilities internal static class SyntaxPrinter { private static readonly SyntaxSerializer _serializer = - new SyntaxSerializer(new SyntaxSerializerOptions { Indented = true }); + new(new SyntaxSerializerOptions { Indented = true }); private static readonly SyntaxSerializer _serializerNoIndent = - new SyntaxSerializer(new SyntaxSerializerOptions { Indented = false }); + new(new SyntaxSerializerOptions { Indented = false }); public static string Print(this ISyntaxNode node, bool indented) { diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxWriterExtensions.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxWriterExtensions.cs index 65b09435500..7096cd2da1b 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxWriterExtensions.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxWriterExtensions.cs @@ -56,7 +56,7 @@ public static void WriteName(this ISyntaxWriter writer, NameNode nameNode) public static void WriteValue( this ISyntaxWriter writer, - IValueNode node) + IValueNode? node) { if (node is null) { diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap index aa37887d0f6..0adf1496ebb 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap @@ -164,6 +164,9 @@ directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | "Annotates the original name of a type." directive @source("The original name of the annotated type." name: Name! "The name of the schema to which this type belongs to." schema: Name!) repeatable on ENUM | OBJECT | INTERFACE | UNION | INPUT_OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD @@ -174,7 +177,7 @@ scalar Byte scalar Date "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "The built-in `Decimal` scalar type." scalar Decimal diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedRedisSchemaTests.AutoMerge_Schema.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedRedisSchemaTests.AutoMerge_Schema.snap index bee8003b0d0..295d2e777dc 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedRedisSchemaTests.AutoMerge_Schema.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedRedisSchemaTests.AutoMerge_Schema.snap @@ -58,11 +58,14 @@ directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | "Annotates the original name of a type." directive @source("The original name of the annotated type." name: Name! "The name of the schema to which this type belongs to." schema: Name!) repeatable on ENUM | OBJECT | INTERFACE | UNION | INPUT_OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("Streamed when true." if: Boolean! "The initial elements that shall be send down to the consumer." initialCount: Int! "If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "The name scalar represents a valid GraphQL name as specified in the spec and can be used to refer to fields or types." scalar Name diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaErrorTests.AutoMerge_Schema.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaErrorTests.AutoMerge_Schema.snap index a9161ac1336..faf1836dbe9 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaErrorTests.AutoMerge_Schema.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaErrorTests.AutoMerge_Schema.snap @@ -61,11 +61,14 @@ directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | "Annotates the original name of a type." directive @source("The original name of the annotated type." name: Name! "The name of the schema to which this type belongs to." schema: Name!) repeatable on ENUM | OBJECT | INTERFACE | UNION | INPUT_OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "The name scalar represents a valid GraphQL name as specified in the spec and can be used to refer to fields or types." scalar Name diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaTests.AutoMerge_Schema.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaTests.AutoMerge_Schema.snap index 198e1ab21fa..b8be32d34d4 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaTests.AutoMerge_Schema.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/FederatedSchemaTests.AutoMerge_Schema.snap @@ -58,11 +58,14 @@ directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | "Annotates the original name of a type." directive @source("The original name of the annotated type." name: Name! "The name of the schema to which this type belongs to." schema: Name!) repeatable on ENUM | OBJECT | INTERFACE | UNION | INPUT_OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "The name scalar represents a valid GraphQL name as specified in the spec and can be used to refer to fields or types." scalar Name diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.ContractSchemaSnapshot.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.ContractSchemaSnapshot.snap index 159ed1d19e6..67d84813d58 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.ContractSchemaSnapshot.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.ContractSchemaSnapshot.snap @@ -58,6 +58,9 @@ directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREA "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip("Skipped when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD @@ -68,7 +71,7 @@ scalar Byte scalar Date "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "The built-in `Decimal` scalar type." scalar Decimal diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/BuiltInTypes.cs b/src/HotChocolate/Utilities/src/Utilities.Introspection/BuiltInTypes.cs index d6e9bb5df05..85ee772e96c 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/BuiltInTypes.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/BuiltInTypes.cs @@ -8,7 +8,7 @@ namespace HotChocolate.Utilities.Introspection public static class BuiltInTypes { private static readonly HashSet _typeNames = - new HashSet + new() { __Directive, __DirectiveLocation, @@ -26,13 +26,14 @@ public static class BuiltInTypes }; private static readonly HashSet _directiveNames = - new HashSet + new() { Skip, Include, Deprecated, Defer, - Stream + Stream, + SpecifiedBy }; public static bool IsBuiltInType(string name) => _typeNames.Contains(name); diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/WellKnownDirectives.cs b/src/HotChocolate/Utilities/src/Utilities.Introspection/WellKnownDirectives.cs index f612fc93fea..998255e1237 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/WellKnownDirectives.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/WellKnownDirectives.cs @@ -7,6 +7,7 @@ internal static class WellKnownDirectives public const string Defer = "defer"; public const string Stream = "stream"; public const string Deprecated = "deprecated"; + public const string SpecifiedBy = "specifiedBy"; public const string DeprecationReasonArgument = "reason"; } }