Skip to content

Commit

Permalink
Reworked the version handling of Apollo Federation. (#6839)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Jan 19, 2024
1 parent 179234f commit bb04265
Show file tree
Hide file tree
Showing 38 changed files with 578 additions and 738 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Collections.Generic;
using HotChocolate.Types.Descriptors;
using HotChocolate.Types.Descriptors.Definitions;

namespace HotChocolate.ApolloFederation;

internal static class FederationVersionExtensions
{
private static readonly Dictionary<Uri, FederationVersion> _uriToVersion = new()
{
[new Uri(FederationVersionUrls.Federation20)] = FederationVersion.Federation20,
[new Uri(FederationVersionUrls.Federation21)] = FederationVersion.Federation21,
[new Uri(FederationVersionUrls.Federation22)] = FederationVersion.Federation22,
[new Uri(FederationVersionUrls.Federation23)] = FederationVersion.Federation23,
[new Uri(FederationVersionUrls.Federation24)] = FederationVersion.Federation24,
[new Uri(FederationVersionUrls.Federation25)] = FederationVersion.Federation25,
};

private static readonly Dictionary<FederationVersion, Uri> _versionToUri = new()
{
[FederationVersion.Federation20] = new(FederationVersionUrls.Federation20),
[FederationVersion.Federation21] = new(FederationVersionUrls.Federation21),
[FederationVersion.Federation22] = new(FederationVersionUrls.Federation22),
[FederationVersion.Federation23] = new(FederationVersionUrls.Federation23),
[FederationVersion.Federation24] = new(FederationVersionUrls.Federation24),
[FederationVersion.Federation25] = new(FederationVersionUrls.Federation25),
};

public static FederationVersion GetFederationVersion<T>(
this IDescriptor<T> descriptor)
where T : DefinitionBase
{
var contextData = descriptor.Extend().Context.ContextData;
if (contextData.TryGetValue(FederationContextData.FederationVersion, out var value) &&
value is FederationVersion version and > FederationVersion.Unknown)
{
return version;
}

// TODO : resources
throw new InvalidOperationException("The configuration state is invalid.");
}

public static FederationVersion GetFederationVersion(
this IDescriptorContext context)
{
if (context.ContextData.TryGetValue(FederationContextData.FederationVersion, out var value) &&
value is FederationVersion version and > FederationVersion.Unknown)
{
return version;
}

// TODO : resources
throw new InvalidOperationException("The configuration state is invalid.");
}

public static Uri ToUrl(this FederationVersion version)
{
if(_versionToUri.TryGetValue(version, out var url))
{
return url;
}

// TODO : resources
throw new ArgumentException("The federation version is not supported.", nameof(version));
}

public static FederationVersion ToVersion(this Uri url)
{
if(_uriToVersion.TryGetValue(url, out var version))
{
return version;
}

// TODO : resources
throw new ArgumentException("The federation url is not supported.", nameof(url));
}

public static bool TryToVersion(this Uri url, out FederationVersion version)
=> _uriToVersion.TryGetValue(url, out version);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ internal static class FederationContextData
public const string ExternalSetter = "HotChocolate.ApolloFederation.ExternalSetter";
public const string EntityResolver = "HotChocolate.ApolloFederation.EntityResolver";
public const string FederationVersion = "HotChocolate.ApolloFederation.Version";
public const string ExportedDirectives = "HotChocolate.ApolloFederation.ExportedDirectives";
public const string DataField = "data";
public const string TypeField = "__type";
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ internal sealed class FederationTypeInterceptor : TypeInterceptor
BindingFlags.Static | BindingFlags.Public)!;

private readonly List<ObjectType> _entityTypes = new();
private readonly Dictionary<string, HashSet<string>> _imports = new();
private readonly Dictionary<Uri, HashSet<string>> _imports = new();
private IDescriptorContext _context = default!;
private ITypeInspector _typeInspector = default!;
private TypeRegistry _typeRegistry = default!;
private ObjectType _queryType = default!;
private ExtendedTypeDirectiveReference _keyDirectiveReference = default!;
private SchemaTypeDefinition _schemaTypeDefinition = default!;
Expand All @@ -57,6 +58,7 @@ internal override void InitializeContext(
{
_typeInspector = context.TypeInspector;
_context = context;
_typeRegistry = typeRegistry;
_keyDirectiveReference = new ExtendedTypeDirectiveReference(_typeInspector.GetType(typeof(KeyDirective)));
ModifyOptions(context, o => o.Mode = TagMode.ApolloFederation);
}
Expand Down Expand Up @@ -102,7 +104,7 @@ public override IEnumerable<TypeReference> RegisterMoreTypes(
yield return _typeInspector.GetTypeRef(typeof(LinkDirective));
}
}

public override void OnBeforeCompleteName(
ITypeCompletionContext completionContext,
DefinitionBase definition)
Expand All @@ -125,7 +127,7 @@ public override void OnAfterCompleteName(

var hasRuntimeType = (IHasRuntimeType)definition;
var type = hasRuntimeType.RuntimeType;

if (type != typeof(object) &&
type.IsDefined(typeof(PackageAttribute)))
{
Expand All @@ -134,6 +136,7 @@ public override void OnAfterCompleteName(
}

type = completionContext.Type.GetType();

if (type.IsDefined(typeof(PackageAttribute)))
{
RegisterImport(type);
Expand Down Expand Up @@ -167,22 +170,105 @@ void RegisterImport(MemberInfo element)
}

public override void OnTypesCompletedName()
{
RegisterExportedDirectives();
RegisterImports();
}

private void RegisterImports()
{
if (_imports.Count == 0)
{
return;
}

var version = _context.GetFederationVersion();
var federationTypes = new HashSet<string>();

foreach (var import in _imports)
{
if (!import.Key.TryToVersion(out var importVersion))
{
continue;
}

if (importVersion > version)
{
// todo: throw helper
throw new SchemaException(
SchemaErrorBuilder.New()
.SetMessage(
"The following federation types were used and are not supported by " +
"the current federation version: {0}",
string.Join(", ", import.Value))
.Build());
}

federationTypes.UnionWith(import.Value);
}

if (version == FederationVersion.Federation10)
{
return;
}

var dependency = new TypeDependency(
_typeInspector.GetTypeRef(typeof(LinkDirective)),
TypeDependencyFulfilled.Completed);
_schemaType.Dependencies.Add(dependency);

_schemaTypeDefinition
.GetLegacyDefinition()
.AddDirective(
new LinkDirective(version.ToUrl(), federationTypes),
_typeInspector);

foreach (var import in _imports)
{
if (import.Key.TryToVersion(out _))
{
continue;
}

_schemaTypeDefinition
.GetLegacyDefinition()
.AddDirective(new LinkDirective(new Uri(import.Key), import.Value), _typeInspector);
.AddDirective(
new LinkDirective(import.Key, import.Value),
_typeInspector);
}
}

private void RegisterExportedDirectives()
{
if (!_context.ContextData.TryGetValue(ExportedDirectives, out var value) ||
value is not List<Type> exportedDirectives)
{
return;
}

var composeDirectives = new List<ComposeDirective>();
foreach (var exportedDirective in exportedDirectives)
{
var typeReference = _typeInspector.GetTypeRef(exportedDirective);
if (_typeRegistry.TryGetType(typeReference, out var exportedDirectiveType))
{
composeDirectives.Add(new ComposeDirective(exportedDirectiveType.Type.Name));
}
}

if (composeDirectives.Count > 0)
{
var dependency = new TypeDependency(
_typeInspector.GetTypeRef(typeof(ComposeDirective)),
TypeDependencyFulfilled.Completed);
_schemaType.Dependencies.Add(dependency);

foreach (var directive in composeDirectives)
{
_schemaTypeDefinition
.GetLegacyDefinition()
.AddDirective(directive, _typeInspector);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public enum FederationVersion
Federation23 = 2_3,
Federation24 = 2_4,
Federation25 = 2_5,
Federation26 = 2_6,
Latest = Federation26,
// Federation26 = 2_6,
Latest = Federation25,
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,15 @@
<Compile Update="Types\Directives\RequiresScopesDescriptorExtensions.cs">
<DependentUpon>RequiresScopesDirective.cs</DependentUpon>
</Compile>
<Compile Update="Types\Directives\FederationResources.Designer.cs">
<Compile Update="Properties\FederationResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>FederationResources.resx</DependentUpon>
</Compile>
<EmbeddedResource Update="Properties\FederationResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>FederationResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="Types\Directives\RequiresScopesAttribute.cs">
<DependentUpon>RequiresScopesDirective.cs</DependentUpon>
</Compile>
Expand All @@ -119,15 +123,4 @@
<None Include="$(MSBuildThisFileDirectory)..\MSBuild\HotChocolate.ApolloFederation.targets" Pack="true" PackagePath="build/HotChocolate.ApolloFederation.targets" Visible="false" />
</ItemGroup>

<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Types\Directives\FederationResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>FederationResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Reflection;
using HotChocolate.ApolloFederation.Types;
using HotChocolate.Types.Descriptors;

namespace HotChocolate.ApolloFederation;
namespace HotChocolate.ApolloFederation.Types;

/// <summary>
/// <code>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using HotChocolate.ApolloFederation.Constants;
using static HotChocolate.ApolloFederation.FederationTypeNames;
using static HotChocolate.ApolloFederation.FederationVersionUrls;
using static HotChocolate.ApolloFederation.Properties.FederationResources;

namespace HotChocolate.ApolloFederation;
namespace HotChocolate.ApolloFederation.Types;

/// <summary>
/// <code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using static HotChocolate.ApolloFederation.FederationTypeNames;
using static HotChocolate.ApolloFederation.Properties.FederationResources;

namespace HotChocolate.ApolloFederation;
namespace HotChocolate.ApolloFederation.Types;

/// <summary>
/// <code>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using HotChocolate.Execution.Configuration;
using Microsoft.Extensions.DependencyInjection;
using static HotChocolate.ApolloFederation.FederationContextData;

namespace HotChocolate.ApolloFederation.Types;

Expand Down Expand Up @@ -51,4 +53,27 @@ public static IRequestExecutorBuilder ExportDirective(

return builder;
}

public static IRequestExecutorBuilder ExportDirective<T>(
this IRequestExecutorBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

builder.AddType<T>();
builder.AddType<ComposeDirective>();

builder.ConfigureSchema(
sb =>
{
if(!sb.ContextData.TryGetValue(ExportedDirectives, out var directiveTypes))
{
directiveTypes = new List<Type>();
sb.ContextData[ExportedDirectives] = directiveTypes;
}

((List<Type>)directiveTypes!).Add(typeof(T));
});

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using static HotChocolate.ApolloFederation.FederationTypeNames;

namespace HotChocolate.ApolloFederation;
namespace HotChocolate.ApolloFederation.Types;

/// <summary>
/// Object representation of @link directive.
Expand Down
Loading

0 comments on commit bb04265

Please sign in to comment.