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 @@ -30,7 +30,7 @@ protected override void Format(IBufferWriter<byte> snapshot, OperationResult val
writer.WriteNumber("variableIndex", value.VariableIndex.Value);
}

if (value.Data.ValueKind is JsonValueKind.Object)
if (value.Data.ValueKind is JsonValueKind.Object or JsonValueKind.Null)
{
writer.WritePropertyName("data");
value.Data.WriteTo(writer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ private void RegisterImports()
_schemaTypeCfg
.GetLegacyConfiguration()
.AddDirective(
new LinkDirective(version.ToUrl(), federationTypes),
new LinkDirective(version.ToUrl(), federationTypes.Order().ToArray()),
_typeInspector);

foreach (var import in _imports)
Expand All @@ -287,7 +287,7 @@ private void RegisterImports()
_schemaTypeCfg
.GetLegacyConfiguration()
.AddDirective(
new LinkDirective(import.Key, import.Value),
new LinkDirective(import.Key, import.Value.Order().ToArray()),
_typeInspector);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public static IRequestExecutorBuilder AddLink(
builder.ConfigureSchema(
sb =>
sb.AddSchemaConfiguration(
d => d.Directive(new LinkDirective(url, imports?.ToHashSet()))));
d => d.Directive(new LinkDirective(url, imports?.Distinct().Order().ToArray()))));

return builder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ public sealed class LinkDirective
/// <param name="import">
/// Optional list of imported elements.
/// </param>
public LinkDirective(Uri url, IReadOnlySet<string>? import)
public LinkDirective(Uri url, IReadOnlyList<string>? import)
{
Url = url;
Import = import;
var imports = import?.ToArray();

if (imports is not null)
{
Array.Sort(imports, StringComparer.Ordinal);
}

Import = imports;
}

/// <summary>
Expand All @@ -36,5 +43,5 @@ public LinkDirective(Uri url, IReadOnlySet<string>? import)
/// Gets optional list of imported element names.
/// </summary>
[GraphQLDescription(FederationResources.LinkDirective_Import_Description)]
public IReadOnlySet<string>? Import { get; }
public IReadOnlyList<string>? Import { get; }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@tag", "@key", "@provides", "@external", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@external", "@key", "@provides", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@tag", "@key", "@provides", "@external", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@external", "@key", "@provides", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@provides", "@external", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@external", "@key", "@provides", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@provides", "@external", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@external", "@key", "@provides", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema
@composeDirective(name: "@custom")
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@tag", "FieldSet", "@composeDirective"])
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@composeDirective", "@key", "@tag", "FieldSet"])
@link(url: "https://specs.custom.dev/custom/v1.0", import: ["@custom"]) {
query: Query
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema
@composeDirective(name: "@custom")
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@tag", "FieldSet", "@composeDirective"])
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@composeDirective", "@key", "@tag", "FieldSet"])
@link(url: "https://specs.custom.dev/custom/v1.0", import: ["@custom"]) {
query: Query
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@external", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@external", "@key", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@tag", "FieldSet", "@policy"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@policy", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@tag", "FieldSet", "@policy"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@policy", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@policy", "@key", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@policy", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@provides", "@key", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@provides", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@requires", "@key", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@requires", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@tag", "FieldSet", "@requiresScopes"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@requiresScopes", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@tag", "FieldSet", "@requiresScopes"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@requiresScopes", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@requiresScopes", "@key", "@tag", "FieldSet"]) {
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@requiresScopes", "@tag", "FieldSet"]) {
query: Query
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System.Diagnostics.CodeAnalysis;
using HotChocolate.Types;

namespace HotChocolate;

/// <summary>
/// Provides extension methods for <see cref="ISchemaDefinition"/>.
/// </summary>
public static class SchemaDefinitionExtensions
{
/// <summary>
/// Resolves a <see cref="ITypeSystemMember"/> by its <see cref="SchemaCoordinate"/>.
/// </summary>
/// <param name="schema">
/// The schema definition to resolve against.
/// </param>
/// <param name="coordinate">
/// The schema coordinate to resolve.
/// </param>
/// <returns>
/// The resolved type system member.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown when no type system member exists for the given <paramref name="coordinate"/>.
/// </exception>
public static ITypeSystemMember GetMember(
this ISchemaDefinition schema,
SchemaCoordinate coordinate)
{
if (!schema.TryGetMember(coordinate, out var member))
{
throw new InvalidOperationException(
$"Failed to resolve schema coordinate '{coordinate}'.");
}

return member;
}

/// <summary>
/// Tries to resolve a <see cref="ITypeSystemMember"/> by its <see cref="SchemaCoordinate"/>.
/// </summary>
/// <param name="schema">
/// The schema definition to resolve against.
/// </param>
/// <param name="coordinate">
/// The schema coordinate to resolve.
/// </param>
/// <param name="member">
/// The resolved type system definition.
/// </param>
/// <returns>
/// <c>true</c> if a type system definition was found with the given
/// <paramref name="coordinate"/>; otherwise, <c>false</c>.
/// </returns>
public static bool TryGetMember(
this ISchemaDefinition schema,
SchemaCoordinate coordinate,
[NotNullWhen(true)] out ITypeSystemMember? member)
{
if (coordinate.OfDirective)
{
if (!schema.DirectiveDefinitions.TryGetDirective(coordinate.Name, out var directive))
{
member = null;
return false;
}

if (coordinate.ArgumentName is not null)
{
if (directive.Arguments.TryGetField(coordinate.ArgumentName, out var arg))
{
member = arg;
return true;
}

member = null;
return false;
}

member = directive;
return true;
}

if (!schema.Types.TryGetType(coordinate.Name, out var type))
{
member = null;
return false;
}

if (coordinate.MemberName is null)
{
member = type;
return true;
}

switch (type)
{
case IComplexTypeDefinition complexType:
if (!complexType.Fields.TryGetField(coordinate.MemberName, out var field))
{
member = null;
return false;
}

if (coordinate.ArgumentName is not null)
{
if (field.Arguments.TryGetField(coordinate.ArgumentName, out var fieldArg))
{
member = fieldArg;
return true;
}

member = null;
return false;
}

member = field;
return true;

case IEnumTypeDefinition enumType:
if (enumType.Values.TryGetValue(coordinate.MemberName, out var enumValue))
{
member = enumValue;
return true;
}

member = null;
return false;

case IInputObjectTypeDefinition inputType:
if (inputType.Fields.TryGetField(coordinate.MemberName, out var inputField))
{
member = inputField;
return true;
}

member = null;
return false;

default:
member = null;
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
namespace HotChocolate;

/// <summary>
/// Provides semantic search capabilities over a GraphQL schema,
/// allowing consumers to find schema elements by natural language queries
/// and to discover paths from a given schema coordinate back to a root type.
/// </summary>
public interface ISchemaSearchProvider
{
/// <summary>
/// Searches the schema for elements matching the specified query.
/// </summary>
/// <param name="query">
/// The search query string.
/// </param>
/// <param name="first">
/// The maximum number of results to return.
/// </param>
/// <param name="after">
/// An opaque cursor for forward pagination.
/// Pass <c>null</c> to start from the beginning.
/// </param>
/// <param name="minScore">
/// The minimum relevance score in the range [0.0, 1.0].
/// Results with a score below this threshold are excluded.
/// Pass <c>null</c> to include all results.
/// </param>
/// <param name="cancellationToken">
/// The cancellation token.
/// </param>
/// <returns>
/// A list of <see cref="SchemaSearchResult"/> ordered by relevance.
/// </returns>
/// <exception cref="InvalidSearchCursorException">
/// Thrown when the <paramref name="after"/> cursor is malformed or out of range.
/// </exception>
/// <exception cref="SearchQueryTooLargeException">
/// Thrown when the <paramref name="query"/> exceeds the maximum allowed length.
/// </exception>
ValueTask<IReadOnlyList<SchemaSearchResult>> SearchAsync(
string query,
int first,
string? after,
float? minScore,
CancellationToken cancellationToken = default);

/// <summary>
/// Gets the paths from the specified schema coordinate to a root type.
/// The implementation determines how many paths to return.
/// </summary>
/// <param name="coordinate">
/// The schema coordinate from which to trace paths to a root type.
/// </param>
/// <param name="cancellationToken">
/// The cancellation token.
/// </param>
/// <returns>
/// A list of <see cref="SchemaCoordinatePath"/> instances,
/// each representing an ordered path from the coordinate to a root type,
/// ordered by path length (shortest first).
/// </returns>
ValueTask<IReadOnlyList<SchemaCoordinatePath>> GetPathsToRootAsync(
SchemaCoordinate coordinate,
CancellationToken cancellationToken = default);
}
Loading
Loading