Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"description": "The `Any` scalar type represents any valid GraphQL value."
},
"base64String": {
"pattern": "\"^(?:[A-Za-z0-9+\\\\/]{4})*(?:[A-Za-z0-9+\\\\/]{2}==|[A-Za-z0-9+\\\\/]{3}=)?$\"",
"pattern": "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)?$",
"type": "string",
"description": "The `Base64String` scalar type represents an array of bytes encoded as a Base64 string."
},
Expand All @@ -83,14 +83,16 @@
"format": "int32"
},
"date": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": "string",
"description": "The `Date` scalar type represents a date in UTC."
"description": "The `Date` scalar type represents a date in UTC.",
"format": "date"
},
"dateTime": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}[Tt]\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?(?:[Zz]|[+-]\\\\d{2}:\\\\d{2})$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:[Zz]|[+-]\\d{2}:\\d{2})$",
"type": "string",
"description": "The `DateTime` scalar type represents a date and time with time zone offset information."
"description": "The `DateTime` scalar type represents a date and time with time zone offset information.",
"format": "date-time"
},
"decimal": {
"type": "number",
Expand Down Expand Up @@ -152,17 +154,18 @@
}
},
"localDate": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": "string",
"description": "The `LocalDate` scalar type represents a date without time or time zone information."
"description": "The `LocalDate` scalar type represents a date without time or time zone information.",
"format": "date"
},
"localDateTime": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}[Tt]\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?$",
"type": "string",
"description": "The `LocalDateTime` scalar type represents a date and time without time zone information."
},
"localTime": {
"pattern": "\"^\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?$\"",
"pattern": "^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?$",
"type": "string",
"description": "The `LocalTime` scalar type represents a time of day without date or time zone information."
},
Expand Down Expand Up @@ -190,7 +193,7 @@
"type": "object",
"properties": {
"field1C": {
"pattern": "\"^\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?$\"",
"pattern": "^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?$",
"type": "string",
"description": "field1C description"
}
Expand All @@ -211,25 +214,29 @@
"type": "string"
},
"timeSpan": {
"pattern": "\"^-?P(?:\\\\d+W|(?=\\\\d|T(?:\\\\d|$))(?:\\\\d+Y)?(?:\\\\d+M)?(?:\\\\d+D)?(?:T(?:\\\\d+H)?(?:\\\\d+M)?(?:\\\\d+(?:\\\\.\\\\d+)?S)?)?)$\"",
"pattern": "^-?P(?:\\d+W|(?=\\d|T(?:\\d|$))(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?:\\d+H)?(?:\\d+M)?(?:\\d+(?:\\.\\d+)?S)?)?)$",
"type": "string",
"description": "The `TimeSpan` scalar type represents a duration of time."
"description": "The `TimeSpan` scalar type represents a duration of time.",
"format": "duration"
},
"unknown": {
"type": "string"
},
"uri": {
"type": "string",
"description": "The `URI` scalar type represents a Uniform Resource Identifier (URI) as defined by RFC 3986."
"description": "The `URI` scalar type represents a Uniform Resource Identifier (URI) as defined by RFC 3986.",
"format": "uri"
},
"url": {
"type": "string",
"description": "The `URL` scalar type represents a Uniform Resource Locator (URL) as defined by RFC 3986."
"description": "The `URL` scalar type represents a Uniform Resource Locator (URL) as defined by RFC 3986.",
"format": "uri"
},
"uuid": {
"pattern": "\"^[\\\\da-fA-F]{8}-[\\\\da-fA-F]{4}-[\\\\da-fA-F]{4}-[\\\\da-fA-F]{4}-[\\\\da-fA-F]{12}$\"",
"pattern": "^[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}$",
"type": "string",
"description": "The `UUID` scalar type represents a Universally Unique Identifier (UUID) as defined by RFC 9562."
"description": "The `UUID` scalar type represents a Universally Unique Identifier (UUID) as defined by RFC 9562.",
"format": "uuid"
}
}
}
Expand Down Expand Up @@ -295,7 +302,7 @@
"description": "The `Any` scalar type represents any valid GraphQL value."
},
"base64String": {
"pattern": "\"^(?:[A-Za-z0-9+\\\\/]{4})*(?:[A-Za-z0-9+\\\\/]{2}==|[A-Za-z0-9+\\\\/]{3}=)?$\"",
"pattern": "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)?$",
"type": [
"null",
"string"
Expand All @@ -317,20 +324,22 @@
"format": "int32"
},
"date": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": [
"null",
"string"
],
"description": "The `Date` scalar type represents a date in UTC."
"description": "The `Date` scalar type represents a date in UTC.",
"format": "date"
},
"dateTime": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}[Tt]\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?(?:[Zz]|[+-]\\\\d{2}:\\\\d{2})$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:[Zz]|[+-]\\d{2}:\\d{2})$",
"type": [
"null",
"string"
],
"description": "The `DateTime` scalar type represents a date and time with time zone offset information."
"description": "The `DateTime` scalar type represents a date and time with time zone offset information.",
"format": "date-time"
},
"decimal": {
"type": [
Expand Down Expand Up @@ -410,23 +419,24 @@
}
},
"localDate": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": [
"null",
"string"
],
"description": "The `LocalDate` scalar type represents a date without time or time zone information."
"description": "The `LocalDate` scalar type represents a date without time or time zone information.",
"format": "date"
},
"localDateTime": {
"pattern": "\"^\\\\d{4}-\\\\d{2}-\\\\d{2}[Tt]\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?$\"",
"pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?$",
"type": [
"null",
"string"
],
"description": "The `LocalDateTime` scalar type represents a date and time without time zone information."
},
"localTime": {
"pattern": "\"^\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?$\"",
"pattern": "^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?$",
"type": [
"null",
"string"
Expand Down Expand Up @@ -469,7 +479,7 @@
],
"properties": {
"field1C": {
"pattern": "\"^\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d{1,9})?$\"",
"pattern": "^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?$",
"type": [
"null",
"string"
Expand Down Expand Up @@ -497,12 +507,13 @@
]
},
"timeSpan": {
"pattern": "\"^-?P(?:\\\\d+W|(?=\\\\d|T(?:\\\\d|$))(?:\\\\d+Y)?(?:\\\\d+M)?(?:\\\\d+D)?(?:T(?:\\\\d+H)?(?:\\\\d+M)?(?:\\\\d+(?:\\\\.\\\\d+)?S)?)?)$\"",
"pattern": "^-?P(?:\\d+W|(?=\\d|T(?:\\d|$))(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?:\\d+H)?(?:\\d+M)?(?:\\d+(?:\\.\\d+)?S)?)?)$",
"type": [
"null",
"string"
],
"description": "The `TimeSpan` scalar type represents a duration of time."
"description": "The `TimeSpan` scalar type represents a duration of time.",
"format": "duration"
},
"unknown": {
"type": [
Expand All @@ -515,15 +526,17 @@
"null",
"string"
],
"description": "The `URL` scalar type represents a Uniform Resource Locator (URL) as defined by RFC 3986."
"description": "The `URL` scalar type represents a Uniform Resource Locator (URL) as defined by RFC 3986.",
"format": "uri"
},
"uuid": {
"pattern": "\"^[\\\\da-fA-F]{8}-[\\\\da-fA-F]{4}-[\\\\da-fA-F]{4}-[\\\\da-fA-F]{4}-[\\\\da-fA-F]{12}$\"",
"pattern": "^[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}$",
"type": [
"null",
"string"
],
"description": "The `UUID` scalar type represents a Universally Unique Identifier (UUID) as defined by RFC 9562."
"description": "The `UUID` scalar type represents a Universally Unique Identifier (UUID) as defined by RFC 9562.",
"format": "uuid"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Collections.Immutable;
using HotChocolate.Fusion.Directives;
using HotChocolate.Fusion.Extensions;
using HotChocolate.Fusion.Info;
using HotChocolate.Fusion.Options;
using HotChocolate.Types;
using HotChocolate.Types.Mutable;
using ArgumentNames = HotChocolate.Fusion.WellKnownArgumentNames;
using DirectiveNames = HotChocolate.Fusion.WellKnownDirectiveNames;

namespace HotChocolate.Fusion.DirectiveMergers;

internal class SpecifiedByDirectiveMerger(DirectiveMergeBehavior mergeBehavior)
: DirectiveMergerBase(mergeBehavior)
{
public override string DirectiveName => DirectiveNames.SpecifiedBy;

public override MutableDirectiveDefinition GetCanonicalDirectiveDefinition(ISchemaDefinition schema)
{
return SpecifiedByMutableDirectiveDefinition.Create(schema);
}

public override void MergeDirectives(
IDirectivesProvider mergedMember,
ImmutableArray<DirectivesProviderInfo> memberDefinitions,
MutableSchemaDefinition mergedSchema)
{
if (!mergedSchema.DirectiveDefinitions.TryGetDirective(DirectiveName, out var directiveDefinition))
{
// Merged definition not found.
return;
}

var specifiedByDirectives =
memberDefinitions
.SelectMany(d => d.Member.Directives.Where(dir => dir.Name == DirectiveNames.SpecifiedBy))
.Select(SpecifiedByDirective.From)
.ToArray();

if (specifiedByDirectives.Length == 0)
{
return;
}

var specifiedByDirective =
new Directive(
directiveDefinition,
new ArgumentAssignment(ArgumentNames.Url, specifiedByDirectives[0].Url));

mergedMember.AddDirective(specifiedByDirective);
Comment thread
glen-84 marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using HotChocolate.Language;
using HotChocolate.Types;
using static HotChocolate.Fusion.Properties.CompositionResources;
using ArgumentNames = HotChocolate.Types.DirectiveNames.SpecifiedBy.Arguments;

namespace HotChocolate.Fusion.Directives;

internal sealed class SpecifiedByDirective(string url)
{
public string Url { get; } = url;

public static SpecifiedByDirective From(IDirective directive)
{
if (!directive.Arguments.TryGetValue(ArgumentNames.Url, out var urlArg)
|| urlArg is not StringValueNode url)
{
throw new InvalidOperationException(SpecifiedByDirective_UrlArgument_Invalid);
}

return new SpecifiedByDirective(url.Value);
}
}

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

Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,9 @@
<data name="ShareableMutableDirectiveDefinition_Description" xml:space="preserve">
<value>The @shareable directive allows multiple source schemas to define the same field, ensuring that this decision is both intentional and coordinated by requiring fields to be explicitly marked.</value>
</data>
<data name="SpecifiedByDirective_UrlArgument_Invalid" xml:space="preserve">
<value>The @specifiedBy directive must have a 'url' argument of type String.</value>
</data>
<data name="TagDirective_NameArgument_Invalid" xml:space="preserve">
<value>The @tag directive must have a 'name' argument of type String.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public SourceSchemaMerger(
DirectiveNames.SerializeAs,
new SerializeAsDirectiveMerger(DirectiveMergeBehavior.Include)
},
{
DirectiveNames.SpecifiedBy,
new SpecifiedByDirectiveMerger(DirectiveMergeBehavior.Include)
},
{
DirectiveNames.Tag,
new TagDirectiveMerger(_options.TagMergeBehavior)
Expand Down Expand Up @@ -925,6 +929,8 @@ private MutableInterfaceTypeDefinition MergeInterfaceTypes(
.MergeDirectives(scalarType, memberDefinitions, mergedSchema);
_directiveMergers[DirectiveNames.SerializeAs]
.MergeDirectives(scalarType, memberDefinitions, mergedSchema);
_directiveMergers[DirectiveNames.SpecifiedBy]
.MergeDirectives(scalarType, memberDefinitions, mergedSchema);
_directiveMergers[DirectiveNames.Tag]
.MergeDirectives(scalarType, memberDefinitions, mergedSchema);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ internal static class WellKnownArgumentNames
public const string SlicingArguments = "slicingArguments";
public const string SourceType = "sourceType";
public const string Type = "type";
public const string Url = "url";
public const string Vary = "vary";
public const string Weight = "weight";
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ internal static class WellKnownDirectiveNames
public const string Require = DirectiveNames.Require.Name;
public const string SerializeAs = DirectiveNames.SerializeAs.Name;
public const string Shareable = DirectiveNames.Shareable.Name;
public const string SpecifiedBy = DirectiveNames.SpecifiedBy.Name;
public const string Tag = DirectiveNames.Tag.Name;
}
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ private static void CompleteScalarType(

if (specifiedByDirective is not null)
{
if (specifiedByDirective.Arguments["url"].Value is not StringValueNode url)
if (specifiedByDirective.Arguments["url"] is not StringValueNode url)
Comment thread
glen-84 marked this conversation as resolved.
{
throw new InvalidOperationException("The specified type does not have a url.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,26 @@ directive @require(field: FieldSelectionMap!) on ARGUMENT_DEFINITION
],
"");
}

[Fact]
public void Merge_ScalarsWithSpecifiedBy_MatchesSnapshot()
{
AssertMatches(
[
"""
# Schema A
scalar Date
""",
"""
# Schema B
scalar Date @specifiedBy(url: "https://example.com/date-spec")
"""
],
"""
scalar Date
@specifiedBy(url: "https://example.com/date-spec")
@fusion__type(schema: A)
@fusion__type(schema: B)
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ internal OneOfMutableDirectiveDefinition()
Locations = DirectiveLocation.InputObject;
}

public static OneOfMutableDirectiveDefinition Create(ISchemaDefinition schema)
public static OneOfMutableDirectiveDefinition Create(ISchemaDefinition _)
{
return new OneOfMutableDirectiveDefinition();
}
Expand Down
Loading
Loading