Skip to content

Commit

Permalink
edits
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Jan 19, 2024
1 parent 26943d1 commit ea594fb
Show file tree
Hide file tree
Showing 10 changed files with 4,903 additions and 15 deletions.
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 @@ -42,6 +42,7 @@ internal sealed class FederationTypeInterceptor : TypeInterceptor
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 @@ -168,6 +170,12 @@ void RegisterImport(MemberInfo element)
}

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

private void RegisterImports()
{
if (_imports.Count == 0)
{
Expand Down Expand Up @@ -195,7 +203,7 @@ public override void OnTypesCompletedName()
string.Join(", ", import.Value))
.Build());
}

federationTypes.UnionWith(import.Value);
}

Expand All @@ -208,11 +216,11 @@ public override void OnTypesCompletedName()
_typeInspector.GetTypeRef(typeof(LinkDirective)),
TypeDependencyFulfilled.Completed);
_schemaType.Dependencies.Add(dependency);

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

foreach (var import in _imports)
Expand All @@ -225,11 +233,45 @@ public override void OnTypesCompletedName()
_schemaTypeDefinition
.GetLegacyDefinition()
.AddDirective(
new LinkDirective(import.Key, import.Value),
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);
}
}
}

internal override void OnAfterResolveRootType(
ITypeCompletionContext completionContext,
ObjectTypeDefinition definition,
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
@@ -0,0 +1,122 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using CookieCrumble;
using HotChocolate.ApolloFederation.Constants;
using HotChocolate.ApolloFederation.Types;
using HotChocolate.Execution;
using HotChocolate.Language;
using HotChocolate.Types;
using HotChocolate.Types.Descriptors;
using Microsoft.Extensions.DependencyInjection;
using DirectiveLocation = HotChocolate.Types.DirectiveLocation;

namespace HotChocolate.ApolloFederation;

public class ComposeDirectiveTests
{
[Fact]
public async Task TestServiceTypeEmptyQueryTypePureCodeFirst()
{
// arrange
var schema = await new ServiceCollection()
.AddGraphQL()
.AddApolloFederation()
.AddQueryType()
.AddType<Address>()
.ExportDirective<Custom>()
.BuildSchemaAsync();

var entityType = schema.GetType<ObjectType>(FederationTypeNames.ServiceType_Name);
var sdlResolver = entityType.Fields[WellKnownFieldNames.Sdl].Resolver!;

// act
var value = await sdlResolver(TestHelper.CreateResolverContext(schema));

Utf8GraphQLParser
.Parse((string)value!)
.MatchSnapshot();
}

[Key("field")]
public class Address
{
[CustomDirective]
public string Field => "abc";
}

[Package("https://specs.custom.dev/custom/v1.0")]
[DirectiveType(DirectiveLocation.FieldDefinition)]
public sealed class Custom;

public sealed class CustomDirectiveAttribute : DirectiveAttribute<Custom>
{
public CustomDirectiveAttribute()
: base(new Custom())
{
}
}

public abstract class DirectiveAttribute<T>(T directive) : DescriptorAttribute where T : class
{
protected override void TryConfigure(
IDescriptorContext context,
IDescriptor descriptor,
ICustomAttributeProvider element)
{
switch (descriptor)
{
case ArgumentDescriptor desc:
desc.Directive(directive);
break;

case DirectiveArgumentDescriptor desc:
desc.Directive(directive);
break;

case EnumTypeDescriptor desc:
desc.Directive(directive);
break;

case EnumValueDescriptor desc:
desc.Directive(directive);
break;

case InputFieldDescriptor desc:
desc.Directive(directive);
break;

case InputObjectTypeDescriptor desc:
desc.Directive(directive);
break;

case InterfaceFieldDescriptor desc:
desc.Directive(directive);
break;

case InterfaceTypeDescriptor desc:
desc.Directive(directive);
break;

case ObjectFieldDescriptor desc:
desc.Directive(directive);
break;

case ObjectTypeDescriptor desc:
desc.Directive(directive);
break;

case SchemaTypeDescriptor desc:
desc.Directive(directive);
break;

case UnionTypeDescriptor desc:
desc.Directive(directive);
break;

default:
throw new ArgumentOutOfRangeException(nameof(descriptor));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
schema @composeDirective(name: "custom") @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.5", import: [ "@composeDirective", "@key", "FieldSet" ]) @link(url: "https:\/\/specs.custom.dev\/custom\/v1.0", import: [ "@custom" ]) {
query: Query
}

type Address @key(fields: "field") {
field: String! @custom
}

type Query {
_service: _Service!
_entities(representations: [_Any!]!): [_Entity]!
}

"This type provides a field named sdl: String! which exposes the SDL of the service's schema. This SDL (schema definition language) is a printed version of the service's schema including the annotations of federation directives. This SDL does not include the additions of the federation spec."
type _Service {
sdl: String!
}

"Union of all types that key directive applied. This information is needed by the Apollo federation gateway."
union _Entity = Address

"Marks underlying custom directive to be included in the Supergraph schema."
directive @composeDirective(name: String!) repeatable on SCHEMA

directive @custom on FIELD_DEFINITION

"Used to indicate a combination of fields that can be used to uniquely identify and fetch an object or interface."
directive @key(fields: FieldSet! resolvable: Boolean = true) repeatable on OBJECT | INTERFACE

"Object representation of @link directive."
directive @link("Gets imported specification url." url: String! "Gets optional list of imported element names." import: [String!]) repeatable on SCHEMA

"Scalar representing a set of fields."
scalar FieldSet

"The _Any scalar is used to pass representations of entities from external services into the root _entities field for execution. Validation of the _Any scalar is done by matching the __typename and @external fields defined in the schema."
scalar _Any
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<ProjectReference Include="..\..\src\ApolloFederation\HotChocolate.ApolloFederation.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public async Task TestServiceTypeTypePureCodeFirst()
.Parse((string)value!)
.MatchInlineSnapshot(
"""
schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.5", import: [ "@key", "FieldSet" ]) {
schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.2", import: [ "@key", "FieldSet" ]) {
query: Query
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"xunit.core": "2.4.1"
}
},
"System.ComponentModel.Annotations": {
"type": "Direct",
"requested": "[5.0.0, )",
"resolved": "5.0.0",
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
},
"xunit": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -448,11 +454,6 @@
"resolved": "7.0.0",
"contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ=="
},
"System.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
},
"System.Console": {
"type": "Transitive",
"resolved": "4.3.0",
Expand Down Expand Up @@ -1541,6 +1542,12 @@
"xunit.core": "2.4.1"
}
},
"System.ComponentModel.Annotations": {
"type": "Direct",
"requested": "[5.0.0, )",
"resolved": "5.0.0",
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
},
"xunit": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -1997,11 +2004,6 @@
"resolved": "8.0.0",
"contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
},
"System.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
},
"System.Console": {
"type": "Transitive",
"resolved": "4.3.0",
Expand Down
Loading

0 comments on commit ea594fb

Please sign in to comment.