Skip to content

Commit

Permalink
Added Schema Coordinates (#4582)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Staib <[email protected]>
  • Loading branch information
PascalSenn and michaelstaib authored Dec 22, 2021
1 parent 3217ebd commit aed915f
Show file tree
Hide file tree
Showing 136 changed files with 2,832 additions and 209 deletions.

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 @@ -198,4 +198,10 @@
<data name="ThrowHelper_TryRewriteNullability_InvalidNullabilityStructure" xml:space="preserve">
<value>The nullability modifier does not match the field type structure.</value>
</data>
<data name="ThrowHelper_SchemaCoordinate_ArgumentNameCannotBeSetWithoutMemberName" xml:space="preserve">
<value>A argument name without a member name is only allowed on directive coordinates.</value>
</data>
<data name="ThrowHelper_SchemaCoordinate_MemberNameCannotBeSetOnADirectiveCoordinate" xml:space="preserve">
<value>A directive cannot contain a member name.</value>
</data>
</root>
170 changes: 170 additions & 0 deletions src/HotChocolate/Core/src/Abstractions/SchemaCoordinate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using System;
using System.Diagnostics.CodeAnalysis;
using HotChocolate.Language;
using static HotChocolate.Properties.AbstractionResources;

namespace HotChocolate;

/// <summary>
/// A <see cref="SchemaCoordinate"/> is a human readable string that uniquely identifies a
/// schema element within a GraphQL Schema.
/// A schema element is a specific instance of a named type, field, input field, enum value,
/// field argument, directive, or directive argument.
/// A <see cref="SchemaCoordinate"/> is always unique. Each schema element may be referenced
/// by exactly one possible schema coordinate.
///
/// A <see cref="SchemaCoordinate"/> may refer to either a defined or built-in schema element.
/// For example, `String` and `@deprecated(reason:)` are both valid schema coordinates which refer
/// to built-in schema elements. However it must not refer to a meta-field.
/// For example, `Business.__typename` is <b>not</b> a valid schema coordinate.
///
/// SchemaCoordinate :
/// - Name
/// - Name . Name
/// - Name . Name ( Name : )
/// - @ Name
/// - @ Name ( Name : )
/// </summary>
public readonly struct SchemaCoordinate
{
/// <summary>
/// Creates a new instance of <see cref="SchemaCoordinate"/>
/// </summary>
/// <exception cref="ArgumentNullException">
/// The <paramref name="name"/> is <c>null</c> or <see cref="String.Empty" />.
/// </exception>
/// <exception cref="ArgumentException">
/// - A directive cannot contain a <paramref name="memberName"/>.
/// - A <paramref name="argumentName"/>. without a <paramref name="memberName"/> is only allowed
/// on directive coordinates.
/// </exception>
public SchemaCoordinate(
NameString name,
NameString? memberName = null,
NameString? argumentName = null,
bool ofDirective = false)
{
memberName?.EnsureNotEmpty(nameof(memberName));
argumentName?.EnsureNotEmpty(nameof(argumentName));

if (ofDirective && memberName is not null)
{
throw new ArgumentException(
ThrowHelper_SchemaCoordinate_MemberNameCannotBeSetOnADirectiveCoordinate,
nameof(memberName));
}

if (!ofDirective && memberName is null && argumentName is not null)
{
throw new ArgumentException(
ThrowHelper_SchemaCoordinate_ArgumentNameCannotBeSetWithoutMemberName,
nameof(argumentName));
}

Name = name.EnsureNotEmpty(nameof(name));
MemberName = memberName;
ArgumentName = argumentName;
OfDirective = ofDirective;
}

/// <summary>
/// Specifies if this <see cref="SchemaCoordinateNode"/> is a coordinate of a directive.
/// </summary>
public bool OfDirective { get; }

/// <summary>
/// The name of the referenced <see cref="INamedSyntaxNode"/>
/// </summary>
public NameString Name { get; }

/// <summary>
/// The optional name of the referenced field or enum value
/// </summary>
public NameString? MemberName { get; }

/// <summary>
/// The optional name of the referenced argument
/// </summary>
public NameString? ArgumentName { get; }

/// <summary>
/// Gets the syntax representation of this <see cref="SchemaCoordinate"/>.
/// </summary>
public SchemaCoordinateNode ToSyntax()
{
NameNode? memberName = MemberName is null ? null : new(MemberName.Value);
NameNode? argumentName = ArgumentName is null ? null : new(ArgumentName.Value);

return new(null, OfDirective, new(Name.Value), memberName, argumentName);
}

/// <summary>
/// Gets the string representation of this <see cref="SchemaCoordinate"/>.
/// </summary>
public override string ToString() => ToSyntax().ToString();

/// <summary>
/// Tries to parse a <see cref="SchemaCoordinate"/> from a <see cref="String"/>.
/// </summary>
/// <param name="s">The string that may represent a <see cref="SchemaCoordinate"/>.</param>
/// <param name="coordinate">
/// If the string <paramref name="s"/> represented a valid schema coordinate string this
/// will be the parsed schema coordinate.
/// </param>
/// <returns>
/// <c>true</c> if the string was a valid representation of a schema coordinate.
/// </returns>
public static bool TryParse(
string s,
[NotNullWhen(true)] out SchemaCoordinate? coordinate)
{
if (string.IsNullOrEmpty(s))
{
coordinate = null;
return false;
}

try
{
coordinate = Parse(s);
return true;
}
catch (SyntaxException)
{
coordinate = null;
return false;
}
}

/// <summary>
/// Parses a schema coordinate string representation.
/// </summary>
/// <param name="s">The schema coordinate string representation.</param>
/// <returns>
/// Returns the parses schema coordinate.
/// </returns>
public static SchemaCoordinate Parse(string s)
=> FromSyntax(Utf8GraphQLParser.Syntax.ParseSchemaCoordinate(s));

/// <summary>
/// Creates a <see cref="SchemaCoordinate"/> from a <see cref="SchemaCoordinateNode"/>.
/// </summary>
/// <param name="node">
/// The syntax node.
/// </param>
/// <returns>
/// Returns the <see cref="SchemaCoordinate"/> instance.
/// </returns>
public static SchemaCoordinate FromSyntax(SchemaCoordinateNode node)
{
NameString? memberName = node.MemberName is null
? null
: (NameString?)node.MemberName.Value;

NameString? argumentName = node.ArgumentName is null
? null
: (NameString?)node.ArgumentName.Value;

return new(node.Name.Value, memberName, argumentName, node.OfDirective);
}
}
Loading

0 comments on commit aed915f

Please sign in to comment.