From 85680473414f90ad821a8687c5e08dc4613feb03 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 18 Dec 2023 19:50:31 +0100 Subject: [PATCH] Fixed Tag Composition Tooling (#6783) --- .../Features/TagDirectiveFeature.cs | 17 +-- .../src/Composition/FusionGraphComposer.cs | 2 +- .../Pipeline/ApplyTagDirectiveMiddleware.cs | 106 ++++++++++-------- .../src/Composition/Pipeline/TagContext.cs | 6 +- .../CommandLine.Tests/ComposeCommandTests.cs | 53 ++++++++- ...tChocolate.Fusion.CommandLine.Tests.csproj | 8 +- .../__resources__/test2.gateway-config.json | 10 ++ .../__resources__/test2.graphql | 40 +++++++ .../__resources__/test2.subgraph-config.json | 4 + .../ComposeCommandTests.Compose_With_Tag.snap | 105 +++++++++++++++++ ...e_System_Members_With_Internal_Tag.graphql | 23 +--- ...With_Internal_Tag_Which_Is_Private.graphql | 20 +--- .../ServiceCollectionExtensions.cs | 8 +- 13 files changed, 287 insertions(+), 115 deletions(-) create mode 100644 src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.gateway-config.json create mode 100644 src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.graphql create mode 100644 src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.subgraph-config.json create mode 100644 src/HotChocolate/Fusion/test/CommandLine.Tests/__snapshots__/ComposeCommandTests.Compose_With_Tag.snap diff --git a/src/HotChocolate/Fusion/src/Composition/Features/TagDirectiveFeature.cs b/src/HotChocolate/Fusion/src/Composition/Features/TagDirectiveFeature.cs index 1ebec12768f..d203de798d5 100644 --- a/src/HotChocolate/Fusion/src/Composition/Features/TagDirectiveFeature.cs +++ b/src/HotChocolate/Fusion/src/Composition/Features/TagDirectiveFeature.cs @@ -3,23 +3,18 @@ namespace HotChocolate.Fusion.Composition.Features; /// /// Specifies behavior of the @tag directive. /// -public sealed class TagDirectiveFeature : IFusionFeature +public sealed class TagDirectiveFeature( + IEnumerable? exclude = null, + bool makeTagsPublic = false) + : IFusionFeature { - public TagDirectiveFeature( - IEnumerable? exclude = null, - bool makeTagsPublic = false) - { - Excluded = new HashSet(exclude ?? Enumerable.Empty()); - MakeTagsPublic = makeTagsPublic; - } - /// /// Gets the tags that shall be excluded from the public schema. /// - public IReadOnlySet Excluded { get; } + public IReadOnlySet Excluded { get; } = new HashSet(exclude ?? Enumerable.Empty()); /// /// Defines if the tag directives should be exported to the public schema. /// - public bool MakeTagsPublic { get; } + public bool MakeTagsPublic { get; } = makeTagsPublic; } \ No newline at end of file diff --git a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs index 4d02dcf457c..b255cf4a60d 100644 --- a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs +++ b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs @@ -72,8 +72,8 @@ internal FusionGraphComposer( .Use() .Use() .Use() - .Use() .Use() + .Use() .Use() .Build(); _logFactory = logFactory; diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/ApplyTagDirectiveMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/ApplyTagDirectiveMiddleware.cs index aa6dc2e43e4..9fe8c330ccb 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/ApplyTagDirectiveMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/ApplyTagDirectiveMiddleware.cs @@ -8,21 +8,17 @@ namespace HotChocolate.Fusion.Composition.Pipeline; internal sealed class ApplyTagDirectiveMiddleware : IMergeMiddleware { - public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate next) + public ValueTask InvokeAsync(CompositionContext context, MergeDelegate next) { - if (context.Features.MakeTagsPublic()) - { - Rewrite(context); - } - - if (!context.Log.HasErrors) - { - await next(context); - } + Rewrite(context, context.Features.MakeTagsPublic()); + return !context.Log.HasErrors + ? next(context) + : ValueTask.CompletedTask; } private static void Rewrite( - CompositionContext context) + CompositionContext context, + bool makePublic) { var needsDirectiveType = false; @@ -54,7 +50,7 @@ private static void Rewrite( } var tags = new HashSet(); - Rewrite(context, tagDirectiveType, tags); + Rewrite(context, tagDirectiveType, tags, makePublic); if (context.GetTagContext().HasTags && needsDirectiveType) { @@ -65,38 +61,39 @@ private static void Rewrite( private static void Rewrite( CompositionContext context, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var tagContext = context.GetTagContext(); - ApplyDirectives(tagContext, context.FusionGraph, context.Subgraphs, tagDirectiveType, tags); + ApplyDirectives(tagContext, context.FusionGraph, context.Subgraphs, tagDirectiveType, tags, makePublic); foreach (var type in context.FusionGraph.Types) { switch (type) { case ObjectType objectType: - Rewrite(context, tagContext, objectType, tagDirectiveType, tags); + Rewrite(context, tagContext, objectType, tagDirectiveType, tags, makePublic); break; case InterfaceType interfaceType: - Rewrite(context, tagContext, interfaceType, tagDirectiveType, tags); + Rewrite(context, tagContext, interfaceType, tagDirectiveType, tags, makePublic); break; case UnionType unionType: - Rewrite(context, tagContext, unionType, tagDirectiveType, tags); + Rewrite(context, tagContext, unionType, tagDirectiveType, tags, makePublic); break; case InputObjectType inputObjectType: - Rewrite(context, tagContext, inputObjectType, tagDirectiveType, tags); + Rewrite(context, tagContext, inputObjectType, tagDirectiveType, tags, makePublic); break; case EnumType enumType: - Rewrite(context, tagContext, enumType, tagDirectiveType, tags); + Rewrite(context, tagContext, enumType, tagDirectiveType, tags, makePublic); break; case ScalarType scalarType: - Rewrite(context, tagContext, scalarType, tagDirectiveType, tags); + Rewrite(context, tagContext, scalarType, tagDirectiveType, tags, makePublic); break; default: @@ -106,7 +103,7 @@ private static void Rewrite( foreach (var directiveType in context.FusionGraph.DirectiveTypes) { - Rewrite(context, tagContext, directiveType, tagDirectiveType, tags); + Rewrite(context, tagContext, directiveType, tagDirectiveType, tags, makePublic); } } @@ -115,15 +112,16 @@ private static void Rewrite( TagContext tagContext, ComplexType type, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var coordinate = new SchemaCoordinate(type.Name); - ApplyDirectives(context, tagContext, type, coordinate, tagDirectiveType, tags); + ApplyDirectives(context, tagContext, type, coordinate, tagDirectiveType, tags, makePublic); foreach (var field in type.Fields) { - Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags); + Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags, makePublic); } } @@ -132,23 +130,25 @@ private static void Rewrite( TagContext tagContext, UnionType type, DirectiveType tagDirectiveType, - HashSet tags) - => ApplyDirectives(context, tagContext, type, new SchemaCoordinate(type.Name), tagDirectiveType, tags); + HashSet tags, + bool makePublic) + => ApplyDirectives(context, tagContext, type, new(type.Name), tagDirectiveType, tags, makePublic); private static void Rewrite( CompositionContext context, TagContext tagContext, InputObjectType type, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var coordinate = new SchemaCoordinate(type.Name); - ApplyDirectives(context, tagContext, type, coordinate, tagDirectiveType, tags); + ApplyDirectives(context, tagContext, type, coordinate, tagDirectiveType, tags, makePublic); foreach (var field in type.Fields) { - Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags); + Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags, makePublic); } } @@ -157,15 +157,16 @@ private static void Rewrite( TagContext tagContext, EnumType type, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var coordinate = new SchemaCoordinate(type.Name); - ApplyDirectives(context, tagContext, type, coordinate, tagDirectiveType, tags); + ApplyDirectives(context, tagContext, type, coordinate, tagDirectiveType, tags, makePublic); foreach (var field in type.Values) { - Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags); + Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags, makePublic); } } @@ -174,21 +175,23 @@ private static void Rewrite( TagContext tagContext, ScalarType type, DirectiveType tagDirectiveType, - HashSet tags) - => ApplyDirectives(context, tagContext, type, new SchemaCoordinate(type.Name), tagDirectiveType, tags); + HashSet tags, + bool makePublic) + => ApplyDirectives(context, tagContext, type, new(type.Name), tagDirectiveType, tags, makePublic); private static void Rewrite( CompositionContext context, TagContext tagContext, DirectiveType type, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var coordinate = new SchemaCoordinate(type.Name, ofDirective: true); foreach (var field in type.Arguments) { - Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags); + Rewrite(context, tagContext, field, coordinate, tagDirectiveType, tags, makePublic); } } @@ -198,15 +201,16 @@ private static void Rewrite( OutputField field, SchemaCoordinate parent, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var coordinate = new SchemaCoordinate(parent.Name, field.Name); - ApplyDirectives(context, tagContext, field, coordinate, tagDirectiveType, tags); + ApplyDirectives(context, tagContext, field, coordinate, tagDirectiveType, tags, makePublic); foreach (var argument in field.Arguments) { - Rewrite(context, tagContext, argument, coordinate, tagDirectiveType, tags); + Rewrite(context, tagContext, argument, coordinate, tagDirectiveType, tags, makePublic); } } @@ -216,7 +220,8 @@ private static void Rewrite( InputField field, SchemaCoordinate parent, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) { var coordinate = parent switch { @@ -225,7 +230,7 @@ private static void Rewrite( { MemberName: not null } => new SchemaCoordinate(parent.Name, parent.MemberName, field.Name), }; - ApplyDirectives(context, tagContext, field, coordinate, tagDirectiveType, tags); + ApplyDirectives(context, tagContext, field, coordinate, tagDirectiveType, tags, makePublic); } private static void Rewrite( @@ -234,14 +239,16 @@ private static void Rewrite( EnumValue value, SchemaCoordinate parent, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) => ApplyDirectives( context, tagContext, value, new SchemaCoordinate(parent.Name, value.Name), tagDirectiveType, - tags); + tags, + makePublic); private static void ApplyDirectives( CompositionContext context, @@ -249,11 +256,12 @@ private static void ApplyDirectives( T merged, SchemaCoordinate coordinate, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) where T : ITypeSystemMember, IHasDirectives { var parts = context.GetSubgraphMembers(coordinate); - ApplyDirectives(tagContext, merged, parts, tagDirectiveType, tags); + ApplyDirectives(tagContext, merged, parts, tagDirectiveType, tags, makePublic); foreach (var tag in tags) { @@ -266,7 +274,8 @@ private static void ApplyDirectives( T merged, IEnumerable parts, DirectiveType tagDirectiveType, - HashSet tags) + HashSet tags, + bool makePublic) where T : ITypeSystemMember, IHasDirectives { tags.Clear(); @@ -288,6 +297,11 @@ private static void ApplyDirectives( value is StringValueNode name && tags.Add(name.Value)) { + if (!makePublic) + { + continue; + } + merged.Directives.Add( new Directive( tagDirectiveType, diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/TagContext.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/TagContext.cs index 1e26f7ee9ff..1239fb99658 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/TagContext.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/TagContext.cs @@ -6,7 +6,7 @@ internal sealed class TagContext private readonly Dictionary> _taggedTypes = new(StringComparer.Ordinal); - public bool HasTags { get; set; } = false; + public bool HasTags { get; set; } public void RegisterTagCoordinate(string name, SchemaCoordinate coordinate) { @@ -16,10 +16,10 @@ public void RegisterTagCoordinate(string name, SchemaCoordinate coordinate) } else { - _taggedTypes.Add(name, new HashSet { coordinate }); + _taggedTypes.Add(name, [coordinate]); } } public IReadOnlySet GetTagCoordinates(string name) - => _taggedTypes.TryGetValue(name, out var coordinates) ? coordinates : _empty; + => _taggedTypes.GetValueOrDefault(name, _empty); } \ No newline at end of file diff --git a/src/HotChocolate/Fusion/test/CommandLine.Tests/ComposeCommandTests.cs b/src/HotChocolate/Fusion/test/CommandLine.Tests/ComposeCommandTests.cs index 613901c50d0..bed5257e463 100644 --- a/src/HotChocolate/Fusion/test/CommandLine.Tests/ComposeCommandTests.cs +++ b/src/HotChocolate/Fusion/test/CommandLine.Tests/ComposeCommandTests.cs @@ -193,9 +193,9 @@ await PackageHelper.CreateSubgraphPackageAsync( account.ExtensionFiles)); var packageFile = CreateTempFile(Extensions.FusionPackage); - var settingsFile = System.IO.Path.Combine( - System.IO.Path.GetDirectoryName(packageFile)!, - $"{System.IO.Path.GetFileNameWithoutExtension(packageFile)}-settings.json"); + var settingsFile = Path.Combine( + Path.GetDirectoryName(packageFile)!, + $"{Path.GetFileNameWithoutExtension(packageFile)}-settings.json"); await File.WriteAllTextAsync( settingsFile, @@ -328,4 +328,51 @@ await app.InvokeAsync( snapshot.MatchSnapshot(); } + + [Fact] + public async Task Compose_With_Tag() + { + // arrange + var subgraphDir = CreateTempDir(); + Directory.CreateDirectory(subgraphDir); + + var schemaFile = Path.Combine(subgraphDir, "schema.graphql"); + var configFile = Path.Combine(subgraphDir, "subgraph-config.json"); + + await File.WriteAllTextAsync(schemaFile, FileResource.Open("test2.graphql"), Encoding.UTF8); + await File.WriteAllTextAsync(configFile, FileResource.Open("test2.subgraph-config.json"), Encoding.UTF8); + + var packageFile = CreateTempFile(Extensions.FusionPackage); + var gatewayConfig = Path.Combine( + Path.GetDirectoryName(packageFile)!, + Path.GetFileNameWithoutExtension(packageFile) + "-settings.json"); + File.Delete(packageFile); + + await File.WriteAllTextAsync(gatewayConfig, FileResource.Open("test2.gateway-config.json"), Encoding.UTF8); + + // act + var app = App.CreateBuilder().Build(); + await app.InvokeAsync(new[] { "compose", "-p", packageFile, "-s", subgraphDir }); + + // assert + Assert.True(File.Exists(packageFile)); + + await using var package = FusionGraphPackage.Open(packageFile, FileAccess.Read); + + var fusionGraph = await package.GetFusionGraphAsync(); + var schema = await package.GetSchemaAsync(); + var subgraphs = await package.GetSubgraphConfigurationsAsync(); + + var snapshot = new Snapshot(); + + snapshot.Add(schema, "Schema Document"); + snapshot.Add(fusionGraph, "Fusion Graph Document"); + + foreach (var subgraph in subgraphs) + { + snapshot.Add(subgraph, $"{subgraph.Name} Subgraph Configuration"); + } + + snapshot.MatchSnapshot(); + } } diff --git a/src/HotChocolate/Fusion/test/CommandLine.Tests/HotChocolate.Fusion.CommandLine.Tests.csproj b/src/HotChocolate/Fusion/test/CommandLine.Tests/HotChocolate.Fusion.CommandLine.Tests.csproj index daad547f725..0f12f97b64f 100644 --- a/src/HotChocolate/Fusion/test/CommandLine.Tests/HotChocolate.Fusion.CommandLine.Tests.csproj +++ b/src/HotChocolate/Fusion/test/CommandLine.Tests/HotChocolate.Fusion.CommandLine.Tests.csproj @@ -17,13 +17,9 @@ Always - + Always - - - - - + diff --git a/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.gateway-config.json b/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.gateway-config.json new file mode 100644 index 00000000000..b3d1b1b90f4 --- /dev/null +++ b/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.gateway-config.json @@ -0,0 +1,10 @@ +{ + "fusionTypePrefix": null, + "fusionTypeSelf": false, + "nodeField": { "enabled": true }, + "tagDirective": { + "enabled": true, + "makePublic": false, + "exclude": ["internal"] + } +} diff --git a/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.graphql b/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.graphql new file mode 100644 index 00000000000..a8818a52a97 --- /dev/null +++ b/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.graphql @@ -0,0 +1,40 @@ +type Query { + node(id: ID!): Node + nodes(ids: [ID!]!): [Node]! + userById(id: ID!): User + userByName(name: String!): User + users(first: Int, after: String, last: Int, before: String): UsersConnection +} + +type User implements Node { + id: ID! + name: String! @tag(name: "internal") + displayName: String! + birthdate: String! +} + +interface Node { + id: ID! +} + +type UsersConnection { + pageInfo: PageInfo! + edges: [UsersEdge!] + nodes: [User!] +} + +type UsersEdge { + cursor: String! + node: User! +} + +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + endCursor: String +} + +scalar DateTime + +directive @tag(name: String!) repeatable on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION diff --git a/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.subgraph-config.json b/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.subgraph-config.json new file mode 100644 index 00000000000..0029eede1e7 --- /dev/null +++ b/src/HotChocolate/Fusion/test/CommandLine.Tests/__resources__/test2.subgraph-config.json @@ -0,0 +1,4 @@ +{ + "subgraph": "accounts", + "http": { "baseAddress": "https://localhost:3000/graphql" } +} diff --git a/src/HotChocolate/Fusion/test/CommandLine.Tests/__snapshots__/ComposeCommandTests.Compose_With_Tag.snap b/src/HotChocolate/Fusion/test/CommandLine.Tests/__snapshots__/ComposeCommandTests.Compose_With_Tag.snap new file mode 100644 index 00000000000..565e7d13537 --- /dev/null +++ b/src/HotChocolate/Fusion/test/CommandLine.Tests/__snapshots__/ComposeCommandTests.Compose_With_Tag.snap @@ -0,0 +1,105 @@ +Schema Document +--------------- +schema { + query: Query +} + +type Query { + node(id: ID!): Node + nodes(ids: [ID!]!): [Node]! + userById(id: ID!): User + userByName(name: String!): User + users(after: String before: String first: Int last: Int): UsersConnection +} + +type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String +} + +type User implements Node { + birthdate: String! + displayName: String! + id: ID! +} + +type UsersConnection { + edges: [UsersEdge!] + nodes: [User!] + pageInfo: PageInfo! +} + +type UsersEdge { + cursor: String! + node: User! +} + +interface Node { + id: ID! +} + +scalar DateTime +--------------- + +Fusion Graph Document +--------------- +schema @fusion(version: 1) @transport(subgraph: "accounts", group: "Fusion", location: "https:\/\/localhost:3000\/graphql", kind: "HTTP") @node(subgraph: "accounts", types: [ "User" ]) { + query: Query +} + +type Query { + node(id: ID!): Node @variable(subgraph: "accounts", name: "id", argument: "id") @resolver(subgraph: "accounts", select: "{ node(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) + nodes(ids: [ID!]!): [Node]! @variable(subgraph: "accounts", name: "ids", argument: "ids") @resolver(subgraph: "accounts", select: "{ nodes(ids: $ids) }", arguments: [ { name: "ids", type: "[ID!]!" } ]) + userById(id: ID!): User @variable(subgraph: "accounts", name: "id", argument: "id") @resolver(subgraph: "accounts", select: "{ userById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) + userByName(name: String!): User @variable(subgraph: "accounts", name: "name", argument: "name") @resolver(subgraph: "accounts", select: "{ userByName(name: $name) }", arguments: [ { name: "name", type: "String!" } ]) + users(after: String before: String first: Int last: Int): UsersConnection @variable(subgraph: "accounts", name: "after", argument: "after") @variable(subgraph: "accounts", name: "before", argument: "before") @variable(subgraph: "accounts", name: "first", argument: "first") @variable(subgraph: "accounts", name: "last", argument: "last") @resolver(subgraph: "accounts", select: "{ users(after: $after, before: $before, first: $first, last: $last) }", arguments: [ { name: "after", type: "String" }, { name: "before", type: "String" }, { name: "first", type: "Int" }, { name: "last", type: "Int" } ]) +} + +type PageInfo { + endCursor: String @source(subgraph: "accounts") + hasNextPage: Boolean! @source(subgraph: "accounts") + hasPreviousPage: Boolean! @source(subgraph: "accounts") + startCursor: String @source(subgraph: "accounts") +} + +type User implements Node @variable(subgraph: "accounts", name: "User_id", select: "id") @variable(subgraph: "accounts", name: "User_name", select: "name") @resolver(subgraph: "accounts", select: "{ userById(id: $User_id) }", arguments: [ { name: "User_id", type: "ID!" } ]) @resolver(subgraph: "accounts", select: "{ userByName(name: $User_name) }", arguments: [ { name: "User_name", type: "String!" } ]) @resolver(subgraph: "accounts", select: "{ nodes(ids: $User_id) { ... on User { ... User } } }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") { + birthdate: String! @source(subgraph: "accounts") + displayName: String! @source(subgraph: "accounts") + id: ID! @source(subgraph: "accounts") +} + +type UsersConnection { + edges: [UsersEdge!] @source(subgraph: "accounts") + nodes: [User!] @source(subgraph: "accounts") + pageInfo: PageInfo! @source(subgraph: "accounts") +} + +type UsersEdge { + cursor: String! @source(subgraph: "accounts") + node: User! @source(subgraph: "accounts") +} + +interface Node { + id: ID! +} + +scalar DateTime +--------------- + +accounts Subgraph Configuration +--------------- +{ + "Name": "accounts", + "Schema": "type Query {\n node(id: ID!): Node\n nodes(ids: [ID!]!): [Node]!\n userById(id: ID!): User\n userByName(name: String!): User\n users(first: Int after: String last: Int before: String): UsersConnection\n}\n\ntype User implements Node {\n id: ID!\n name: String! @tag(name: \"internal\")\n displayName: String!\n birthdate: String!\n}\n\ninterface Node {\n id: ID!\n}\n\ntype UsersConnection {\n pageInfo: PageInfo!\n edges: [UsersEdge!]\n nodes: [User!]\n}\n\ntype UsersEdge {\n cursor: String!\n node: User!\n}\n\ntype PageInfo {\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n startCursor: String\n endCursor: String\n}\n\nscalar DateTime\n\ndirective @tag(name: String!) repeatable on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION", + "Extensions": [], + "Clients": [ + { + "ClientName": null, + "BaseAddress": "https://localhost:3000/graphql" + } + ], + "ConfigurationExtensions": null +} +--------------- diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag.graphql b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag.graphql index dc285df44c3..b32a04d247c 100644 --- a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag.graphql +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag.graphql @@ -21,9 +21,6 @@ type Query { @resolver(subgraph: "Reviews", select: "{ reviewOrAuthor }") reviews: [Review!]! @resolver(subgraph: "Reviews", select: "{ reviews }") - someTypeById(id: ID!): SomeType! - @variable(subgraph: "Accounts", name: "id", argument: "id") - @resolver(subgraph: "Accounts", select: "{ someTypeById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) userById(id: ID!): User @variable(subgraph: "Accounts", name: "id", argument: "id") @resolver(subgraph: "Accounts", select: "{ userById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) @@ -42,7 +39,7 @@ type Mutation { addReview(input: AddReviewInput!): AddReviewPayload! @variable(subgraph: "Reviews", name: "input", argument: "input") @resolver(subgraph: "Reviews", select: "{ addReview(input: $input) }", arguments: [ { name: "input", type: "AddReviewInput!" } ]) - addUser(input: AddUserInput!): AddUserPayload! + addUser: AddUserPayload! @variable(subgraph: "Accounts", name: "input", argument: "input") @resolver(subgraph: "Accounts", select: "{ addUser(input: $input) }", arguments: [ { name: "input", type: "AddUserInput!" } ]) } @@ -90,14 +87,6 @@ type SomeData { @source(subgraph: "Accounts") } -type SomeType - @variable(subgraph: "Accounts", name: "SomeType_id", select: "id") - @resolver(subgraph: "Accounts", select: "{ someTypeById(id: $SomeType_id) }", arguments: [ { name: "SomeType_id", type: "ID!" } ]) - @tag(name: "internal") { - id: ID! - @source(subgraph: "Accounts") -} - type User implements Node @source(subgraph: "Reviews", name: "Author") @variable(subgraph: "Accounts", name: "User_id", select: "id") @@ -106,9 +95,6 @@ type User implements Node @resolver(subgraph: "Accounts", select: "{ usersById(ids: $User_id) }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") @resolver(subgraph: "Reviews", select: "{ authorById(id: $User_id) }", arguments: [ { name: "User_id", type: "ID!" } ]) @resolver(subgraph: "Reviews", select: "{ nodes(ids: $User_id) { ... on User { ... User } } }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") { - birthdate: Date! - @source(subgraph: "Accounts") - @tag(name: "internal") id: ID! @source(subgraph: "Accounts") @source(subgraph: "Reviews") @@ -141,13 +127,6 @@ input AddReviewInput { upc: Int! } -input AddUserInput { - birthdate: Date! - @tag(name: "internal") - name: String! - username: String! -} - "The `Date` scalar represents an ISO-8601 compliant date type." scalar Date diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag_Which_Is_Private.graphql b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag_Which_Is_Private.graphql index c5d2932fa6b..772da05906e 100644 --- a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag_Which_Is_Private.graphql +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/TagTests.Exclude_Type_System_Members_With_Internal_Tag_Which_Is_Private.graphql @@ -20,9 +20,6 @@ type Query { @resolver(subgraph: "Reviews", select: "{ reviewOrAuthor }") reviews: [Review!]! @resolver(subgraph: "Reviews", select: "{ reviews }") - someTypeById(id: ID!): SomeType! - @variable(subgraph: "Accounts", name: "id", argument: "id") - @resolver(subgraph: "Accounts", select: "{ someTypeById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) userById(id: ID!): User @variable(subgraph: "Accounts", name: "id", argument: "id") @resolver(subgraph: "Accounts", select: "{ userById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) @@ -41,7 +38,7 @@ type Mutation { addReview(input: AddReviewInput!): AddReviewPayload! @variable(subgraph: "Reviews", name: "input", argument: "input") @resolver(subgraph: "Reviews", select: "{ addReview(input: $input) }", arguments: [ { name: "input", type: "AddReviewInput!" } ]) - addUser(input: AddUserInput!): AddUserPayload! + addUser: AddUserPayload! @variable(subgraph: "Accounts", name: "input", argument: "input") @resolver(subgraph: "Accounts", select: "{ addUser(input: $input) }", arguments: [ { name: "input", type: "AddUserInput!" } ]) } @@ -89,13 +86,6 @@ type SomeData { @source(subgraph: "Accounts") } -type SomeType - @variable(subgraph: "Accounts", name: "SomeType_id", select: "id") - @resolver(subgraph: "Accounts", select: "{ someTypeById(id: $SomeType_id) }", arguments: [ { name: "SomeType_id", type: "ID!" } ]) { - id: ID! - @source(subgraph: "Accounts") -} - type User implements Node @source(subgraph: "Reviews", name: "Author") @variable(subgraph: "Accounts", name: "User_id", select: "id") @@ -104,8 +94,6 @@ type User implements Node @resolver(subgraph: "Accounts", select: "{ usersById(ids: $User_id) }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") @resolver(subgraph: "Reviews", select: "{ authorById(id: $User_id) }", arguments: [ { name: "User_id", type: "ID!" } ]) @resolver(subgraph: "Reviews", select: "{ nodes(ids: $User_id) { ... on User { ... User } } }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") { - birthdate: Date! - @source(subgraph: "Accounts") id: ID! @source(subgraph: "Accounts") @source(subgraph: "Reviews") @@ -138,11 +126,5 @@ input AddReviewInput { upc: Int! } -input AddUserInput { - birthdate: Date! - name: String! - username: String! -} - "The `Date` scalar represents an ISO-8601 compliant date type." scalar Date \ No newline at end of file diff --git a/src/HotChocolate/Utilities/src/Utilities.DependencyInjection/ServiceCollectionExtensions.cs b/src/HotChocolate/Utilities/src/Utilities.DependencyInjection/ServiceCollectionExtensions.cs index 9dd0e63c00f..96b22e9dad4 100644 --- a/src/HotChocolate/Utilities/src/Utilities.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/HotChocolate/Utilities/src/Utilities.DependencyInjection/ServiceCollectionExtensions.cs @@ -9,9 +9,9 @@ public static bool IsImplementationTypeRegistered( this IServiceCollection services) { #if NET8_0_OR_GREATER - return services.All(t => !t.IsKeyedService && t.ImplementationType != typeof(TService)); + return services.Any(t => !t.IsKeyedService && t.ImplementationType == typeof(TService)); #else - return services.All(t => t.ImplementationType != typeof(TService)); + return services.Any(t => t.ImplementationType == typeof(TService)); #endif } @@ -19,9 +19,9 @@ public static bool IsServiceTypeRegistered( this IServiceCollection services) { #if NET8_0_OR_GREATER - return services.All(t => !t.IsKeyedService && t.ServiceType != typeof(TService)); + return services.Any(t => !t.IsKeyedService && t.ServiceType == typeof(TService)); #else - return services.All(t => t.ServiceType != typeof(TService)); + return services.Any(t => t.ServiceType == typeof(TService)); #endif }