diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Directives/FieldDirectiveParser.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Directives/FieldDirectiveParser.cs index 4a837aaeeb0..03a90517b28 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Directives/FieldDirectiveParser.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Directives/FieldDirectiveParser.cs @@ -34,13 +34,19 @@ public static FieldDirective Parse(DirectiveNode directive) break; case "provides": - provides = Utf8GraphQLParser.Syntax.ParseSelectionSet(((StringValueNode)argument.Value).Value); + var providesValue = ((StringValueNode)argument.Value).Value; + provides = ParseProvidesSelectionSet(providesValue); break; case "external": isExternal = ((BooleanValueNode)argument.Value).Value; break; + case "partial": + // `partial` is the composition-time encoding for external source fields. + isExternal = ((BooleanValueNode)argument.Value).Value; + break; + default: throw new DirectiveParserException( $"The argument `{argument.Name.Value}` is not supported on @field."); @@ -56,6 +62,20 @@ public static FieldDirective Parse(DirectiveNode directive) return new FieldDirective(new SchemaKey(schemaKey), sourceName, sourceType, provides, isExternal); } + private static SelectionSetNode ParseProvidesSelectionSet(string value) + { + try + { + // Fusion schemas can encode provides either as a raw selection set (`{ id }`) or as + // the legacy field-set form (`id`). + return Utf8GraphQLParser.Syntax.ParseSelectionSet(value); + } + catch (SyntaxException) + { + return Utf8GraphQLParser.Syntax.ParseSelectionSet($"{{ {value} }}"); + } + } + public static ImmutableArray Parse( IReadOnlyList directiveNodes) { diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/EntityChainTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/EntityChainTests.cs new file mode 100644 index 00000000000..b7c1b5950a5 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/EntityChainTests.cs @@ -0,0 +1,207 @@ +using HotChocolate.Fusion.Types; + +namespace HotChocolate.Fusion.Planning; + +public class EntityChainTests : FusionTestBase +{ + [Fact] + public void Complex_Entity_Call() + { + // arrange + var schema = CreateComplexEntityCallSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + topProducts { + products { + id + price { + price + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Parent_Entity_Call_Complex() + { + // arrange + var schema = CreateParentEntityCallComplexSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + productFromD(id: "1") { + id + name + category { + id + name + details + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + private static FusionSchemaDefinition CreateComplexEntityCallSchema() + { + return ComposeSchema( + """ + # name: products + schema { + query: Query + } + + type Query { + topProducts: ProductList! + } + + type ProductList { + products: [Product!]! + } + + type Product @key(fields: "id") { + id: ID! + category: Category! @shareable + } + + type Category { + id: ID! @shareable + tag: String @shareable + } + """, + """ + # name: link + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + pid: ID! @shareable + } + """, + """ + # name: price + schema { + query: Query + } + + type Query { + productByIdPidAndCategory( + id: ID! @is(field: "id") + pid: ID! @is(field: "pid") + categoryId: ID! @is(field: "category.id") + categoryTag: String @is(field: "category.tag")): Product @lookup @internal + } + + type Product @key(fields: "id pid category { id tag }") { + id: ID! + pid: ID! @shareable + category: Category! @shareable + price: Price + } + + type Category { + id: ID! @shareable + tag: String @shareable + } + + type Price { + price: Float! + } + """); + } + + private static FusionSchemaDefinition CreateParentEntityCallComplexSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + category: Category @shareable + } + + type Category { + details: String + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + category: Category @shareable + } + + type Category @key(fields: "id") { + id: ID! + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + categoryById(id: ID! @is(field: "id")): Category @lookup @internal + } + + type Category @key(fields: "id") { + id: ID! + name: String + } + """, + """ + # name: d + schema { + query: Query + } + + type Query { + productFromD(id: ID!): Product + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + name: String + } + """); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/PlannerAdvancedAdaptationTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/PlannerAdvancedAdaptationTests.cs new file mode 100644 index 00000000000..9421ee3b1fa --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/PlannerAdvancedAdaptationTests.cs @@ -0,0 +1,679 @@ +using HotChocolate.Fusion.Types; + +namespace HotChocolate.Fusion.Planning; + +public class PlannerAdvancedAdaptationTests : FusionTestBase +{ + [Fact] + public void Provides_Simple_Provides() + { + // arrange + var schema = CreateSimpleProvidesSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + reviews { + author { + username + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Provides_Nested_Provides() + { + // arrange + var schema = CreateNestedProvidesSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + id + categories { + id + name + details { + id + name + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Provides_Simple_Requires_Provides() + { + // arrange + var schema = CreateSimpleRequiresProvidesSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + me { + reviews { + id + author { + id + username + } + product { + inStock + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Union_Union_Member_Mix() + { + // arrange + var schema = CreateUnionIntersectionSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + media { + __typename + ... on Book { + title + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Union_Union_Overfetching_Test() + { + // arrange + var schema = CreateUnionOverfetchingSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + review { + ... on AnonymousReview { + product { + b + } + } + ... on UserReview { + product { + c + d + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Issues_Issue_281() + { + // arrange + var schema = CreateIssue281Schema(); + + // act + var plan = PlanOperation( + schema, + """ + { + viewer { + review { + ... on AnonymousReview { + __typename + product { + b + } + } + ... on UserReview { + __typename + product { + c + d + } + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Issues_Issue_190_With_Typename() + { + // arrange + var schema = CreateIssue190Schema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($included: Boolean!) { + recommender @include(if: $included) { + id + results { + ...Recommendable_Product + __typename + } + __typename + } + } + + fragment Recommendable_Product on Product { + id + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Issues_Issue_190_Without_Typename() + { + // arrange + var schema = CreateIssue190Schema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($included: Boolean!) { + recommender @include(if: $included) { + id + results { + ...Recommendable_Product + } + } + } + + fragment Recommendable_Product on Product { + id + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Issues_Issue_190_Inline_Fragment() + { + // arrange + var schema = CreateIssue190Schema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($included: Boolean!) { + recommender @include(if: $included) { + id + results { + ... on Product { + id + } + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + private static FusionSchemaDefinition CreateSimpleProvidesSchema() + { + return ComposeSchema( + """ + # name: products + schema { + query: Query + } + + type Query { + products: [Product] + } + + type Product @key(fields: "upc") { + upc: String! + name: String + } + """, + """ + # name: reviews + schema { + query: Query + } + + type Query { + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + userById(id: ID! @is(field: "id")): User @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + reviews: [Review] + } + + type Review @key(fields: "id") { + id: ID! + author: User @provides(fields: "username") + } + + type User @key(fields: "id") { + id: ID! + username: String @external + } + """, + """ + # name: users + schema { + query: Query + } + + type Query { + userById(id: ID! @is(field: "id")): User @lookup @internal + } + + type User @key(fields: "id") { + id: ID! + username: String + name: String + } + """); + } + + private static FusionSchemaDefinition CreateNestedProvidesSchema() + { + return ComposeSchema( + """ + # name: all-products + schema { + query: Query + } + + type Query { + products: [Product] + } + + type Product @key(fields: "id") { + id: ID! + categories: [Category] + @provides(fields: "id name details { id name }") + } + + type Category @key(fields: "id") { + id: ID! @external + name: String @external + details: CategoryDetails @external + } + + type CategoryDetails { + id: ID! @external + name: String @external + } + """, + """ + # name: category + schema { + query: Query + } + + type Query { + categoryById(id: ID! @is(field: "id")): Category @lookup @internal + } + + type Category @key(fields: "id") { + id: ID! + name: String + details: CategoryDetails + } + + type CategoryDetails { + id: ID! + name: String + } + """); + } + + private static FusionSchemaDefinition CreateSimpleRequiresProvidesSchema() + { + return ComposeSchema( + """ + # name: accounts + schema { + query: Query + } + + type Query { + me: User + userById(id: ID! @is(field: "id")): User @lookup @internal + } + + type User @key(fields: "id") { + id: ID! + username: String + name: String + } + """, + """ + # name: reviews + schema { + query: Query + } + + type Query { + userById(id: ID! @is(field: "id")): User @lookup @internal + } + + type User @key(fields: "id") { + id: ID! + username: String @external + reviews: [Review] + } + + type Review @key(fields: "id") { + id: ID! + author: User @provides(fields: "username") + product: Product + } + + type Product @key(fields: "upc") { + upc: String! + } + """, + """ + # name: inventory + schema { + query: Query + } + + type Query { + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + inStock: Boolean + } + """); + } + + private static FusionSchemaDefinition CreateUnionIntersectionSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + media: Media + } + + union Media = Book | Song + + type Book @key(fields: "id") { + id: ID! + title: String! @shareable + aTitle: String! + } + + type Song @key(fields: "id") { + id: ID! + title: String! + aTitle: String! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + bookById(id: ID! @is(field: "id")): Book @lookup @internal + movieById(id: ID! @is(field: "id")): Movie @lookup @internal + } + + union Media = Book | Movie + + type Book @key(fields: "id") { + id: ID! + title: String! @shareable + bTitle: String! + } + + type Movie @key(fields: "id") { + id: ID! + title: String! + bTitle: String! + } + """); + } + + private static FusionSchemaDefinition CreateUnionOverfetchingSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + review: Review + } + + union Review = AnonymousReview | UserReview + + type UserReview { + product: Product + } + + type AnonymousReview { + product: Product + } + + type Product @key(fields: "id") { + id: ID! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + b: String! + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + c: String! + } + """, + """ + # name: d + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + d: String! + } + """); + } + + private static FusionSchemaDefinition CreateIssue281Schema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + viewer: Viewer! + } + + type Viewer { + review: Review + } + + union Review = AnonymousReview | UserReview + + type UserReview { + product: Product + } + + type AnonymousReview { + product: Product + } + + type Product @key(fields: "id") { + id: ID! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product + @key(fields: "id") + @key(fields: "pid") { + id: ID! + pid: ID! + b: String! + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + productByPid(pid: ID! @is(field: "pid")): Product @lookup @internal + } + + type Product @key(fields: "pid") { + pid: ID! + c: String! + } + """, + """ + # name: d + schema { + query: Query + } + + type Query { + productByPid(pid: ID! @is(field: "pid")): Product @lookup @internal + } + + type Product @key(fields: "pid") { + pid: ID! + d: String! + } + """); + } + + private static FusionSchemaDefinition CreateIssue190Schema() + { + return ComposeSchema( + """ + # name: recommender + schema { + query: Query + } + + type Query { + recommender: Recommender + } + + type Recommender { + id: ID! + results: [Recommendable!]! + } + + interface Recommendable { + id: ID! + } + + type Product implements Recommendable { + id: ID! + } + """); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/PlannerBehaviorTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/PlannerBehaviorTests.cs new file mode 100644 index 00000000000..74853c40b4e --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/PlannerBehaviorTests.cs @@ -0,0 +1,526 @@ +using HotChocolate.Fusion.Types; + +namespace HotChocolate.Fusion.Planning; + +public class PlannerBehaviorTests : FusionTestBase +{ + [Fact] + public void Fragments_Simple_Inline_Fragment() + { + // arrange + var schema = CreateFragmentPlanningSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + price { + amount + currency + } + ... on Product { + isAvailable + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Fragments_Fragment_Spread() + { + // arrange + var schema = CreateFragmentPlanningSchema(); + + // act + var plan = PlanOperation( + schema, + """ + fragment ProductInfo on Product { + isAvailable + } + + query { + products { + price { + amount + currency + } + ...ProductInfo + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Include_Skip_Basic_Include() + { + // arrange + var schema = CreateIncludeSkipSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($include: Boolean) { + product { + price + neverCalledInclude @include(if: $include) + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Include_Skip_Include_Fragment() + { + // arrange + var schema = CreateIncludeSkipSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($include: Boolean) { + product { + price + ... on Product @include(if: $include) { + neverCalledInclude + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Include_Skip_Basic_Skip() + { + // arrange + var schema = CreateIncludeSkipSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($skip: Boolean = false) { + product { + price + skip @skip(if: $skip) + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Include_Skip_Include_At_Root_Fetch() + { + // arrange + var schema = CreateIncludeSkipSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($include: Boolean) { + product { + id + price @include(if: $include) + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Include_Skip_Include_Fragment_At_Root_Fetch() + { + // arrange + var schema = CreateIncludeSkipSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($include: Boolean) { + product { + id + ... on Product @include(if: $include) { + price + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Root_Types_Shared_Root() + { + // arrange + var schema = CreateSharedRootSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + product { + id + name { + id + brand + model + } + category { + id + name + } + price { + id + amount + currency + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Mutations() + { + // arrange + var schema = CreateMutationPlanningSchema(); + + // act + var plan = PlanOperation( + schema, + """ + mutation { + addProduct(input: { name: "new", price: 599.99 }) { + name + price + isExpensive + isAvailable + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Mutations_Many_Fields_Two_Same_Graph() + { + // arrange + var schema = CreateMutationPlanningSchema(); + + // act + var plan = PlanOperation( + schema, + """ + mutation { + five: add(num: 5) + ten: multiply(by: 2) + twelve: add(num: 2) + final: delete + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Mutations_Many_Fields_Two_Same_Graph_Contiguous_Same_Service() + { + // arrange + var schema = CreateMutationPlanningSchema(); + + // act + var plan = PlanOperation( + schema, + """ + mutation { + five: add(num: 5) + seven: add(num: 2) + fourteen: multiply(by: 2) + sixteen: add(num: 2) + final: delete + } + """); + + // assert + MatchSnapshot(plan); + } + + private static FusionSchemaDefinition CreateFragmentPlanningSchema() + { + return ComposeSchema( + """ + # name: store + schema { + query: Query + } + + type Query { + products: [Product] + } + + type Product @key(fields: "id") { + id: ID! @shareable + name: String! + } + """, + """ + # name: info + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product + @key(fields: "id") { + id: ID! @shareable + uuid: ID! @shareable + isAvailable: Boolean! + } + """, + """ + # name: cost + schema { + query: Query + } + + type Query { + productByUuid(uuid: ID! @is(field: "uuid")): Product @lookup @internal + } + + type Product @key(fields: "uuid") { + uuid: ID! @shareable + price: Price + } + + type Price { + amount: Float! + currency: String! + } + """); + } + + private static FusionSchemaDefinition CreateIncludeSkipSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + product: Product + } + + type Product @key(fields: "id") { + id: ID! + price: Float! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + isExpensive(price: Float! @require(field: "price")): Boolean! + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + include(isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + skip(isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + neverCalledInclude( + isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + neverCalledSkip( + isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + } + """); + } + + private static FusionSchemaDefinition CreateSharedRootSchema() + { + return ComposeSchema( + """ + # name: category + schema { + query: Query + } + + type Query { + product: Product! @shareable + } + + type Product { + id: ID! + category: Category! + } + + type Category { + id: ID! + name: String! + } + """, + """ + # name: name + schema { + query: Query + } + + type Query { + product: Product! @shareable + } + + type Product { + name: Name! + } + + type Name { + id: ID! + brand: String! + model: String! + } + """, + """ + # name: price + schema { + query: Query + } + + type Query { + product: Product! @shareable + } + + type Product { + price: Price! + } + + type Price { + id: ID! + amount: Int! + currency: String! + } + """); + } + + private static FusionSchemaDefinition CreateMutationPlanningSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + mutation: Mutation + } + + type Query { + product(id: ID!): Product! + products: [Product!]! + } + + type Mutation { + addProduct(input: AddProductInput!): Product! + multiply(by: Int!): Int! + } + + type Product @key(fields: "id") { + id: ID! + name: String! + price: Float! + } + + input AddProductInput { + name: String! + price: Float! + } + """, + """ + # name: b + schema { + query: Query + mutation: Mutation + } + + type Query { + productById(id: ID! @is(field: "id")): Product @lookup @internal + } + + type Mutation { + delete: Int! + } + + type Product @key(fields: "id") { + id: ID! + isExpensive(price: Float! @require(field: "price")): Boolean! + isAvailable: Boolean! + } + """, + """ + # name: c + schema { + query: Query + mutation: Mutation + } + + type Query { + version: String + } + + type Mutation { + add(num: Int!): Int! + } + """); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementArgumentTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementArgumentTests.cs new file mode 100644 index 00000000000..b27b5038cf3 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementArgumentTests.cs @@ -0,0 +1,441 @@ +using HotChocolate.Fusion.Types; + +namespace HotChocolate.Fusion.Planning; + +public class RequirementArgumentTests : FusionTestBase +{ + [Fact] + public void Fed_Audit_Requires_With_Argument_Conflict() + { + // arrange + var schema = CreateRequiresWithArgumentConflictSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + upc + name + shippingEstimate + shippingEstimateEUR + isExpensiveCategory + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Multiple_Requires_With_Args_That_Conflicts() + { + // arrange + var schema = CreateSimpleRequiresArgsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + test { + id + fieldWithRequiresAndArgs + anotherWithRequiresAndArgs + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Multiple_Plain_Field_And_Requires_With_Args_That_Conflicts() + { + // arrange + var schema = CreateSimpleRequiresArgsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + test { + id + fieldWithRequiresAndArgs + anotherWithRequiresAndArgs + otherField(arg: 1) + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Multiple_Plain_Field_And_Requires_With_Args_That_Does_Not_Conflicts_Should_Merge() + { + // arrange + var schema = CreateSimpleRequiresArgsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + test { + id + fieldWithRequiresAndArgs + otherField + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Arguments_Deeply_Nested_Requires() + { + // arrange + var schema = CreateAuditRequiresArgumentsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + feed { + author { + id + } + comments(limit: 1) { + id + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Arguments_Deeply_Nested_Requires_With_Variable() + { + // arrange + var schema = CreateAuditRequiresArgumentsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($limit: Int = 1) { + feed { + author { + id + } + comments(limit: $limit) { + id + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Arguments_Deeply_Nested_Requires_With_Variables_And_Fragments() + { + // arrange + var schema = CreateAuditRequiresArgumentsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query ($limit: Int = 1) { + feed { + author { + id + } + ...Foo + ...Bar + } + } + + fragment Foo on Post { + comments(limit: $limit) { + id + } + } + + fragment Bar on Post { + comments(limit: $limit) { + id + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_With_Arguments() + { + // arrange + var schema = CreateRequiresWithArgumentsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + feed { + author { + id + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Simple_Requires_Arguments() + { + // arrange + var schema = CreateSimpleRequiresArgsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + test { + id + fieldWithRequiresAndArgs + } + } + """); + + // assert + MatchSnapshot(plan); + } + + private static FusionSchemaDefinition CreateSimpleRequiresArgsSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + test: Test + testById(id: ID! @is(field: "id")): Test @lookup @internal + } + + type Test @key(fields: "id") { + id: ID! + name: String! + fieldWithRequiresAndArgs( + otherField: String! @require(field: "otherField")): String! + fieldWithSameRequiresAndArgs( + otherField: String! @require(field: "otherField")): String! + anotherWithRequiresAndArgs( + otherField: String! @require(field: "otherField")): String! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + testById(id: ID! @is(field: "id")): Test @lookup @internal + } + + type Test @key(fields: "id") { + id: ID! + otherField(arg: Int): String! + } + """); + } + + private static FusionSchemaDefinition CreateAuditRequiresArgumentsSchema() + { + return ComposeSchema( + """ + # name: c + schema { + query: Query + } + + type Query { + feed: [Post] + postById(id: ID! @is(field: "id")): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + comments(limit: Int): [Comment] + } + + type Comment @key(fields: "id") { + id: ID! + authorId: ID + body: String! + } + """, + """ + # name: d + schema { + query: Query + } + + type Query { + postById(id: ID! @is(field: "id")): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + author( + commentAuthorIds: [ID] + @require(field: "comments[authorId]")): Author + } + + type Author { + id: ID! + name: String + } + """); + } + + private static FusionSchemaDefinition CreateRequiresWithArgumentsSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + feed: [Post] + postById(id: ID! @is(field: "id")): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + comments(limit: Int): [Comment] + } + + type Comment @key(fields: "id") { + id: ID! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + postById(id: ID! @is(field: "id")): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + author( + somethingElse: [String] + @require(field: "comments[somethingElse]")): Author + } + + type Author { + id: ID! + name: String + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + commentById(id: ID! @is(field: "id")): Comment @lookup @internal + } + + type Comment @key(fields: "id") { + id: ID! + somethingElse: String + } + """); + } + + private static FusionSchemaDefinition CreateRequiresWithArgumentConflictSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + shippingEstimate( + price: Int @require(field: "price") + weight: Int @require(field: "weight")): Int + shippingEstimateEUR( + price: Int @require(field: "priceEur") + weight: Int @require(field: "weight")): Int + isExpensiveCategory( + averagePrice: Int @require(field: "category.averagePrice")): Boolean + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + products: [Product] + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + categoryById(id: ID! @is(field: "id")): Category @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + name: String + price: Int + priceEur: Int + weight: Int + category: Category + } + + type Category @key(fields: "id") { + id: ID! + averagePrice: Int + } + """); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementChainTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementChainTests.cs new file mode 100644 index 00000000000..0cef4f4705d --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementChainTests.cs @@ -0,0 +1,248 @@ +using HotChocolate.Fusion.Types; + +namespace HotChocolate.Fusion.Planning; + +public class RequirementChainTests : FusionTestBase +{ + [Fact] + public void Requires_Requires_One() + { + // arrange + var schema = CreateRequiresRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + product { + canAffordWithDiscount + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Requires_Many() + { + // arrange + var schema = CreateRequiresRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + product { + id + price + hasDiscount + isExpensive + isExpensiveWithDiscount + canAfford + canAfford2 + canAffordWithDiscount + canAffordWithDiscount2 + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Requires_Two_Fields_Same_Requirement_Different_Order() + { + // arrange + var schema = CreateRequiresRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + product { + canAffordWithAndWithoutDiscount + canAffordWithAndWithoutDiscount2 + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Circular_1() + { + // arrange + var schema = CreateRequiresCircularSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + feed { + byNovice + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_Circular_2() + { + // arrange + var schema = CreateRequiresCircularSchema(); + + // act + var plan = PlanOperation( + schema, + """ + { + feed { + byExpert + } + } + """); + + // assert + MatchSnapshot(plan); + } + + private static FusionSchemaDefinition CreateRequiresRequiresSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + productById(id: ID!): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + price: Float! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + product: Product + productById(id: ID!): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + hasDiscount: Boolean! + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + productById(id: ID!): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + isExpensive(price: Float! @require(field: "price")): Boolean! + isExpensiveWithDiscount( + hasDiscount: Boolean! @require(field: "hasDiscount")): Boolean! + } + """, + """ + # name: d + schema { + query: Query + } + + type Query { + productById(id: ID!): Product @lookup @internal + } + + type Product @key(fields: "id") { + id: ID! + canAfford(isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + canAfford2(isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + canAffordWithDiscount( + isExpensiveWithDiscount: Boolean! + @require(field: "isExpensiveWithDiscount")): Boolean! + canAffordWithDiscount2( + isExpensiveWithDiscount: Boolean! + @require(field: "isExpensiveWithDiscount")): Boolean! + canAffordWithAndWithoutDiscount( + isExpensiveWithDiscount: Boolean! + @require(field: "isExpensiveWithDiscount") + isExpensive: Boolean! @require(field: "isExpensive")): Boolean! + canAffordWithAndWithoutDiscount2( + isExpensive: Boolean! @require(field: "isExpensive") + isExpensiveWithDiscount: Boolean! + @require(field: "isExpensiveWithDiscount")): Boolean! + } + """); + } + + private static FusionSchemaDefinition CreateRequiresCircularSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + feed: [Post] + postById(id: ID!): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + author: Author! + byExpert(byNovice: Boolean! @require(field: "byNovice")): Boolean! + } + + type Author @key(fields: "id") { + id: ID! + name: String! + yearsOfExperience: Int! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + postById(id: ID!): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + byNovice( + yearsOfExperience: Int! + @require(field: "author.yearsOfExperience")): Boolean! + } + """); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementParityTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementParityTests.cs new file mode 100644 index 00000000000..f653878fd24 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/RequirementParityTests.cs @@ -0,0 +1,499 @@ +using HotChocolate.Fusion.Types; + +namespace HotChocolate.Fusion.Planning; + +public class RequirementParityTests : FusionTestBase +{ + [Fact] + public void Simplest_Requires() + { + // arrange + var schema = CreateSimplestRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + isExpensive + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Simple_Requires() + { + // arrange + var schema = CreateSimpleRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + shippingEstimate + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Two_Fields_Same_Subgraph_Same_Requirement() + { + // arrange + var schema = CreateSimpleRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + shippingEstimate + shippingEstimate2 + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Two_Same_Service_Calls_With_Args_Conflicts() + { + // arrange + var schema = CreateTwoSameServiceCallsWithArgsConflictsSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + products { + isExpensive + reducedPrice + price(withDiscount: true) + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Deep_Requires() + { + // arrange + var schema = CreateDeepRequiresSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + feed { + author { + id + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Keys_Mashup() + { + // arrange + var schema = CreateKeysMashupSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + b { + id + a { + id + name + nameInB + } + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Requires_With_Fragments_On_Interfaces() + { + // arrange + var schema = CreateRequiresWithFragmentsOnInterfacesSchema(); + + // act + var plan = PlanOperation( + schema, + """ + query { + userFromA { + permissions + } + } + """); + + // assert + MatchSnapshot(plan); + } + + private static FusionSchemaDefinition CreateDeepRequiresSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + feed: [Post] + } + + type Post @key(fields: "id") { + id: ID! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + postById(id: ID! @is(field: "id")): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + comments(limit: Int!): [Comment] + } + + type Comment @key(fields: "id") { + id: ID! + } + """, + """ + # name: c + schema { + query: Query + } + + type Query { + commentById(id: ID! @is(field: "id")): Comment @lookup @internal + } + + type Comment @key(fields: "id") { + id: ID! + authorId: ID + body: String! + } + """, + """ + # name: d + schema { + query: Query + } + + type Query { + postById(id: ID! @is(field: "id")): Post @lookup @internal + } + + type Post @key(fields: "id") { + id: ID! + author( + commentAuthorIds: [ID] + @require(field: "comments[authorId]")): Author + } + + type Author { + id: ID! + name: String + } + """); + } + + private static FusionSchemaDefinition CreateSimplestRequiresSchema() + { + return ComposeSchema( + """ + # name: products + schema { + query: Query + } + + type Query { + products: [Product] + } + + type Product @key(fields: "upc") { + upc: String! + price: Int + } + """, + """ + # name: inventory + schema { + query: Query + } + + type Query { + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + isExpensive(price: Int @require(field: "price")): Boolean + } + """); + } + + private static FusionSchemaDefinition CreateSimpleRequiresSchema() + { + return ComposeSchema( + """ + # name: products + schema { + query: Query + } + + type Query { + products: [Product] + } + + type Product @key(fields: "upc") { + upc: String! + price: Int + weight: Int + name: String + } + """, + """ + # name: inventory + schema { + query: Query + } + + type Query { + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + shippingEstimate( + price: Int @require(field: "price") + weight: Int @require(field: "weight")): Int + shippingEstimate2( + price: Int @require(field: "price") + weight: Int @require(field: "weight")): String + } + """); + } + + private static FusionSchemaDefinition CreateTwoSameServiceCallsWithArgsConflictsSchema() + { + return ComposeSchema( + """ + # name: inventory + schema { + query: Query + } + + type Query { + products: [Product] + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + isExpensive(price: Int @require(field: "price")): Boolean + reducedPrice(price: Int @require(field: "price")): Boolean + } + """, + """ + # name: products + schema { + query: Query + } + + type Query { + productByUpc(upc: String! @is(field: "upc")): Product @lookup @internal + } + + type Product @key(fields: "upc") { + upc: String! + price(withDiscount: Boolean): Int + } + """); + } + + private static FusionSchemaDefinition CreateKeysMashupSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + aById(id: ID!): A @lookup @internal + } + + type A + @key(fields: "id") + @key(fields: "id compositeId { two three }") { + id: ID! + compositeId: CompositeID! + name: String! + } + + type CompositeID { + two: ID! + three: ID! + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + b: B + aByIdAndCompositeId( + id: ID! @is(field: "id") + compositeIdTwo: ID! @is(field: "compositeId.two") + compositeIdThree: ID! @is(field: "compositeId.three")): A @lookup @internal + } + + type B { + id: ID! + a: [A!]! + } + + type A + @key(fields: "id", resolvable: false) + @key(fields: "id compositeId { two three }", resolvable: true) { + id: ID! + compositeId: CompositeID! + nameInB(name: String! @require(field: "name")): String! + } + + type CompositeID { + two: ID! + three: ID! + } + """); + } + + private static FusionSchemaDefinition CreateRequiresWithFragmentsOnInterfacesSchema() + { + return ComposeSchema( + """ + # name: a + schema { + query: Query + } + + type Query { + userFromA: User + } + + type User @key(fields: "id") { + id: ID! + profile: Profile! @shareable + } + + interface Profile { + displayName: String! + } + + interface Account implements Profile { + displayName: String! + accountType: String! + } + + type GuestAccount implements Profile & Account { + displayName: String! @shareable + accountType: String! @shareable + guestToken: String! @shareable + } + + type AdminAccount implements Profile & Account { + displayName: String! @shareable + accountType: String! @shareable + adminLevel: String! @shareable + } + """, + """ + # name: b + schema { + query: Query + } + + type Query { + userById(id: ID! @is(field: "id")): User @lookup @internal + } + + type User @key(fields: "id") { + id: ID! + profile: Profile! @shareable + permissions( + displayName: String! @require(field: "profile.displayName") + accountType: String + @require(field: + "profile.accountType | profile.accountType") + adminLevel: String + @require(field: "profile.adminLevel") + guestToken: String + @require(field: "profile.guestToken")): String! + } + + interface Profile { + displayName: String! + } + + interface Account implements Profile { + displayName: String! + accountType: String! + } + + type GuestAccount implements Profile & Account { + displayName: String! @shareable + accountType: String! @shareable + guestToken: String! @shareable + } + + type AdminAccount implements Profile & Account { + displayName: String! @shareable + accountType: String! @shareable + adminLevel: String! @shareable + } + """); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/EntityChainTests.Complex_Entity_Call.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/EntityChainTests.Complex_Entity_Call.yaml new file mode 100644 index 00000000000..253ef8b267a --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/EntityChainTests.Complex_Entity_Call.yaml @@ -0,0 +1,90 @@ +operation: + - document: | + { + topProducts { + products { + id + id @fusion__requirement + price { + price + } + pid @fusion__requirement + category @fusion__requirement { + id + tag + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 3 +nodes: + - id: 1 + type: Operation + schema: products + operation: | + query Op_123456789101112_1 { + topProducts { + products { + id + category { + id + tag + } + } + } + } + - id: 2 + type: Operation + schema: price + operation: | + query Op_123456789101112_2( + $__fusion_1_categoryId: ID! + $__fusion_1_categoryTag: String + $__fusion_1_id: ID! + $__fusion_1_pid: ID! + ) { + productByIdPidAndCategory(id: $__fusion_1_id, pid: $__fusion_1_pid, categoryId: $__fusion_1_categoryId, categoryTag: $__fusion_1_categoryTag) { + price { + price + } + } + } + source: $.productByIdPidAndCategory + target: $.topProducts.products + requirements: + - name: __fusion_1_categoryId + selectionMap: >- + category.id + - name: __fusion_1_categoryTag + selectionMap: >- + category.tag + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_1_pid + selectionMap: >- + pid + dependencies: + - id: 1 + - id: 3 + - id: 3 + type: Operation + schema: link + operation: | + query Op_123456789101112_3( + $__fusion_2_id: ID! + ) { + productById(id: $__fusion_2_id) { + pid + } + } + source: $.productById + target: $.topProducts.products + requirements: + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/EntityChainTests.Parent_Entity_Call_Complex.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/EntityChainTests.Parent_Entity_Call_Complex.yaml new file mode 100644 index 00000000000..88e79b98c43 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/EntityChainTests.Parent_Entity_Call_Complex.yaml @@ -0,0 +1,91 @@ +operation: + - document: | + { + productFromD(id: "1") { + id + id @fusion__requirement + name + category { + id + id @fusion__requirement + name + details + } + } + } + hash: 123456789101112 + searchSpace: 2 + expandedNodes: 11 +nodes: + - id: 1 + type: Operation + schema: d + operation: | + query Op_123456789101112_1 { + productFromD(id: "1") { + id + name + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + ) { + productById(id: $__fusion_1_id) { + category { + details + } + } + } + source: $.productById + target: $.productFromD + requirements: + - name: __fusion_1_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 3 + type: Operation + schema: c + operation: | + query Op_123456789101112_3( + $__fusion_2_id: ID! + ) { + categoryById(id: $__fusion_2_id) { + id + name + } + } + source: $.categoryById + target: $.productFromD.category + requirements: + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 4 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + category { + id + } + } + } + source: $.productById + target: $.productFromD + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_Inline_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_Inline_Fragment.yaml new file mode 100644 index 00000000000..bcee156c627 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_Inline_Fragment.yaml @@ -0,0 +1,37 @@ +operation: + - document: | + query( + $included: Boolean! + ) { + recommender @include(if: $included) { + id + results { + __typename @fusion__requirement + ... on Product { + id + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: recommender + operation: | + query Op_123456789101112_1 { + recommender { + id + results { + __typename + ... on Product { + id + } + } + } + } + conditions: + - variable: $included + passingValue: true diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_With_Typename.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_With_Typename.yaml new file mode 100644 index 00000000000..5b552cfc2ce --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_With_Typename.yaml @@ -0,0 +1,39 @@ +operation: + - document: | + query( + $included: Boolean! + ) { + recommender @include(if: $included) { + id + results { + __typename + ... on Product { + id + } + } + __typename + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: recommender + operation: | + query Op_123456789101112_1 { + recommender { + id + results { + __typename + ... on Product { + id + } + } + __typename + } + } + conditions: + - variable: $included + passingValue: true diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_Without_Typename.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_Without_Typename.yaml new file mode 100644 index 00000000000..bcee156c627 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_190_Without_Typename.yaml @@ -0,0 +1,37 @@ +operation: + - document: | + query( + $included: Boolean! + ) { + recommender @include(if: $included) { + id + results { + __typename @fusion__requirement + ... on Product { + id + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: recommender + operation: | + query Op_123456789101112_1 { + recommender { + id + results { + __typename + ... on Product { + id + } + } + } + } + conditions: + - variable: $included + passingValue: true diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_281.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_281.yaml new file mode 100644 index 00000000000..75b598ba935 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Issues_Issue_281.yaml @@ -0,0 +1,129 @@ +operation: + - document: | + { + viewer { + review { + __typename @fusion__requirement + ... on AnonymousReview { + __typename + product { + b + id @fusion__requirement + } + } + ... on UserReview { + __typename + product { + c + d + pid @fusion__requirement + id @fusion__requirement + } + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 5 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + viewer { + review { + __typename + ... on AnonymousReview { + __typename + product { + id + } + } + ... on UserReview { + __typename + product { + id + } + } + } + } + } + - id: 2 + type: Operation + schema: c + operation: | + query Op_123456789101112_2( + $__fusion_1_pid: ID! + ) { + productByPid(pid: $__fusion_1_pid) { + c + pid + } + } + source: $.productByPid + target: $.viewer.review.product + requirements: + - name: __fusion_1_pid + selectionMap: >- + pid + dependencies: + - id: 4 + - id: 3 + type: Operation + schema: d + operation: | + query Op_123456789101112_3( + $__fusion_2_pid: ID! + ) { + productByPid(pid: $__fusion_2_pid) { + d + } + } + source: $.productByPid + target: $.viewer.review.product + requirements: + - name: __fusion_2_pid + selectionMap: >- + pid + dependencies: + - id: 2 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + pid + } + } + source: $.productById + target: $.viewer.review.product + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: b + operation: | + query Op_123456789101112_5( + $__fusion_4_id: ID! + ) { + productById(id: $__fusion_4_id) { + b + } + } + source: $.productById + target: $.viewer.review.product + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Provides_Nested_Provides.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Provides_Nested_Provides.yaml new file mode 100644 index 00000000000..844d6fa6911 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Provides_Nested_Provides.yaml @@ -0,0 +1,36 @@ +operation: + - document: | + { + products { + id + categories { + id + name + details { + id + name + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: all-products + operation: | + query Op_123456789101112_1 { + products { + id + categories { + id + name + details { + id + name + } + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Provides_Simple_Provides.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Provides_Simple_Provides.yaml new file mode 100644 index 00000000000..925252115f7 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Provides_Simple_Provides.yaml @@ -0,0 +1,48 @@ +operation: + - document: | + { + products { + reviews { + author { + username + } + } + upc @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: products + operation: | + query Op_123456789101112_1 { + products { + upc + } + } + - id: 2 + type: Operation + schema: reviews + operation: | + query Op_123456789101112_2( + $__fusion_1_upc: String! + ) { + productByUpc(upc: $__fusion_1_upc) { + reviews { + author { + username + } + } + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_1_upc + selectionMap: >- + upc + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Requires_Provides_Simple_Requires_Provides.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Requires_Provides_Simple_Requires_Provides.yaml new file mode 100644 index 00000000000..d24cd21f061 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Requires_Provides_Simple_Requires_Provides.yaml @@ -0,0 +1,78 @@ +operation: + - document: | + { + me { + reviews { + id + author { + id + username + } + product { + inStock + upc @fusion__requirement + } + } + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 3 +nodes: + - id: 1 + type: Operation + schema: accounts + operation: | + query Op_123456789101112_1 { + me { + id + } + } + - id: 2 + type: Operation + schema: reviews + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + ) { + userById(id: $__fusion_1_id) { + reviews { + id + author { + id + username + } + product { + upc + } + } + } + } + source: $.userById + target: $.me + requirements: + - name: __fusion_1_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 3 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_3( + $__fusion_2_upc: String! + ) { + productByUpc(upc: $__fusion_2_upc) { + inStock + } + } + source: $.productByUpc + target: $.me.reviews.product + requirements: + - name: __fusion_2_upc + selectionMap: >- + upc + dependencies: + - id: 2 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Union_Union_Member_Mix.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Union_Union_Member_Mix.yaml new file mode 100644 index 00000000000..db1ca924a77 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Union_Union_Member_Mix.yaml @@ -0,0 +1,26 @@ +operation: + - document: | + { + media { + __typename + ... on Book { + title + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + media { + __typename + ... on Book { + title + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Union_Union_Overfetching_Test.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Union_Union_Overfetching_Test.yaml new file mode 100644 index 00000000000..da90398c6d8 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerAdvancedAdaptationTests.Union_Union_Overfetching_Test.yaml @@ -0,0 +1,100 @@ +operation: + - document: | + { + review { + __typename @fusion__requirement + ... on AnonymousReview { + product { + b + id @fusion__requirement + } + } + ... on UserReview { + product { + c + d + id @fusion__requirement + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 3 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + review { + __typename + ... on AnonymousReview { + product { + id + } + } + ... on UserReview { + product { + id + } + } + } + } + - id: 2 + type: Operation + schema: c + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + ) { + productById(id: $__fusion_1_id) { + c + } + } + source: $.productById + target: $.review.product + requirements: + - name: __fusion_1_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 3 + type: Operation + schema: d + operation: | + query Op_123456789101112_3( + $__fusion_2_id: ID! + ) { + productById(id: $__fusion_2_id) { + d + } + } + source: $.productById + target: $.review.product + requirements: + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + b + } + } + source: $.productById + target: $.review.product + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Fragments_Fragment_Spread.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Fragments_Fragment_Spread.yaml new file mode 100644 index 00000000000..a88fdb95c47 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Fragments_Fragment_Spread.yaml @@ -0,0 +1,86 @@ +operation: + - document: | + { + products { + price { + amount + currency + } + isAvailable + uuid @fusion__requirement + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 5 +nodes: + - id: 1 + type: Operation + schema: store + operation: | + query Op_123456789101112_1 { + products { + id + } + } + - id: 2 + type: Operation + schema: cost + operation: | + query Op_123456789101112_2( + $__fusion_1_uuid: ID! + ) { + productByUuid(uuid: $__fusion_1_uuid) { + price { + amount + currency + } + } + } + source: $.productByUuid + target: $.products + requirements: + - name: __fusion_1_uuid + selectionMap: >- + uuid + dependencies: + - id: 4 + - id: 3 + type: Operation + schema: info + operation: | + query Op_123456789101112_3( + $__fusion_2_id: ID! + ) { + productById(id: $__fusion_2_id) { + isAvailable + } + } + source: $.productById + target: $.products + requirements: + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: info + operation: | + query Op_123456789101112_4( + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + uuid + } + } + source: $.productById + target: $.products + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Fragments_Simple_Inline_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Fragments_Simple_Inline_Fragment.yaml new file mode 100644 index 00000000000..a88fdb95c47 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Fragments_Simple_Inline_Fragment.yaml @@ -0,0 +1,86 @@ +operation: + - document: | + { + products { + price { + amount + currency + } + isAvailable + uuid @fusion__requirement + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 5 +nodes: + - id: 1 + type: Operation + schema: store + operation: | + query Op_123456789101112_1 { + products { + id + } + } + - id: 2 + type: Operation + schema: cost + operation: | + query Op_123456789101112_2( + $__fusion_1_uuid: ID! + ) { + productByUuid(uuid: $__fusion_1_uuid) { + price { + amount + currency + } + } + } + source: $.productByUuid + target: $.products + requirements: + - name: __fusion_1_uuid + selectionMap: >- + uuid + dependencies: + - id: 4 + - id: 3 + type: Operation + schema: info + operation: | + query Op_123456789101112_3( + $__fusion_2_id: ID! + ) { + productById(id: $__fusion_2_id) { + isAvailable + } + } + source: $.productById + target: $.products + requirements: + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: info + operation: | + query Op_123456789101112_4( + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + uuid + } + } + source: $.productById + target: $.products + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Basic_Include.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Basic_Include.yaml new file mode 100644 index 00000000000..1c49930a8ad --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Basic_Include.yaml @@ -0,0 +1,77 @@ +operation: + - document: | + query( + $include: Boolean + ) { + product { + price + price @fusion__requirement + id @fusion__requirement + isExpensive @fusion__requirement + neverCalledInclude @include(if: $include) + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 8 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + product { + price + id + } + } + - id: 3 + type: Operation + schema: c + operation: | + query Op_123456789101112_3( + $__fusion_2_isExpensive: Boolean! + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + neverCalledInclude(isExpensive: $__fusion_2_isExpensive) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_2_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_3_id + selectionMap: >- + id + conditions: + - variable: $include + passingValue: true + dependencies: + - id: 1 + - id: 4 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + $__fusion_5_price: Float! + ) { + productById(id: $__fusion_4_id) { + isExpensive(price: $__fusion_5_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_4_id + selectionMap: >- + id + - name: __fusion_5_price + selectionMap: >- + price + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Basic_Skip.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Basic_Skip.yaml new file mode 100644 index 00000000000..254ba0b142e --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Basic_Skip.yaml @@ -0,0 +1,77 @@ +operation: + - document: | + query( + $skip: Boolean = false + ) { + product { + price + price @fusion__requirement + id @fusion__requirement + isExpensive @fusion__requirement + skip @skip(if: $skip) + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 8 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + product { + price + id + } + } + - id: 3 + type: Operation + schema: c + operation: | + query Op_123456789101112_3( + $__fusion_2_isExpensive: Boolean! + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + skip(isExpensive: $__fusion_2_isExpensive) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_2_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_3_id + selectionMap: >- + id + conditions: + - variable: $skip + passingValue: false + dependencies: + - id: 1 + - id: 4 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + $__fusion_5_price: Float! + ) { + productById(id: $__fusion_4_id) { + isExpensive(price: $__fusion_5_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_4_id + selectionMap: >- + id + - name: __fusion_5_price + selectionMap: >- + price + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_At_Root_Fetch.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_At_Root_Fetch.yaml new file mode 100644 index 00000000000..9e06d4af83b --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_At_Root_Fetch.yaml @@ -0,0 +1,28 @@ +operation: + - document: | + query( + $include: Boolean + ) { + product { + id + price @include(if: $include) + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1( + $include: Boolean + ) { + product { + id + price @include(if: $include) + } + } + forwardedVariables: + - include diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_Fragment.yaml new file mode 100644 index 00000000000..1c49930a8ad --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_Fragment.yaml @@ -0,0 +1,77 @@ +operation: + - document: | + query( + $include: Boolean + ) { + product { + price + price @fusion__requirement + id @fusion__requirement + isExpensive @fusion__requirement + neverCalledInclude @include(if: $include) + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 8 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + product { + price + id + } + } + - id: 3 + type: Operation + schema: c + operation: | + query Op_123456789101112_3( + $__fusion_2_isExpensive: Boolean! + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + neverCalledInclude(isExpensive: $__fusion_2_isExpensive) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_2_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_3_id + selectionMap: >- + id + conditions: + - variable: $include + passingValue: true + dependencies: + - id: 1 + - id: 4 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + $__fusion_5_price: Float! + ) { + productById(id: $__fusion_4_id) { + isExpensive(price: $__fusion_5_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_4_id + selectionMap: >- + id + - name: __fusion_5_price + selectionMap: >- + price + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_Fragment_At_Root_Fetch.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_Fragment_At_Root_Fetch.yaml new file mode 100644 index 00000000000..9e06d4af83b --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Include_Skip_Include_Fragment_At_Root_Fetch.yaml @@ -0,0 +1,28 @@ +operation: + - document: | + query( + $include: Boolean + ) { + product { + id + price @include(if: $include) + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1( + $include: Boolean + ) { + product { + id + price @include(if: $include) + } + } + forwardedVariables: + - include diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations.yaml new file mode 100644 index 00000000000..eb3b9cea3b8 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations.yaml @@ -0,0 +1,51 @@ +operation: + - document: | + mutation { + addProduct(input: { name: "new", price: 599.99 }) { + name + price + price @fusion__requirement + isExpensive + isAvailable + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + mutation Op_123456789101112_1 { + addProduct(input: { name: "new", price: 599.99 }) { + name + price + id + } + } + - id: 2 + type: Operation + schema: b + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_price: Float! + ) { + productById(id: $__fusion_1_id) { + isAvailable + isExpensive(price: $__fusion_2_price) + } + } + source: $.productById + target: $.addProduct + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_price + selectionMap: >- + price + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations_Many_Fields_Two_Same_Graph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations_Many_Fields_Two_Same_Graph.yaml new file mode 100644 index 00000000000..be21ae76598 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations_Many_Fields_Two_Same_Graph.yaml @@ -0,0 +1,40 @@ +operation: + - document: | + mutation { + five: add(num: 5) + ten: multiply(by: 2) + twelve: add(num: 2) + final: delete + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: c + operation: | + mutation Op_123456789101112_1 { + five: add(num: 5) + } + - id: 2 + type: Operation + schema: a + operation: | + mutation Op_123456789101112_2 { + ten: multiply(by: 2) + } + - id: 3 + type: Operation + schema: c + operation: | + mutation Op_123456789101112_3 { + twelve: add(num: 2) + } + - id: 4 + type: Operation + schema: b + operation: | + mutation Op_123456789101112_4 { + final: delete + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations_Many_Fields_Two_Same_Graph_Contiguous_Same_Service.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations_Many_Fields_Two_Same_Graph_Contiguous_Same_Service.yaml new file mode 100644 index 00000000000..0b8ac38d836 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Mutations_Many_Fields_Two_Same_Graph_Contiguous_Same_Service.yaml @@ -0,0 +1,48 @@ +operation: + - document: | + mutation { + five: add(num: 5) + seven: add(num: 2) + fourteen: multiply(by: 2) + sixteen: add(num: 2) + final: delete + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 1 +nodes: + - id: 1 + type: Operation + schema: c + operation: | + mutation Op_123456789101112_1 { + five: add(num: 5) + } + - id: 2 + type: Operation + schema: c + operation: | + mutation Op_123456789101112_2 { + seven: add(num: 2) + } + - id: 3 + type: Operation + schema: a + operation: | + mutation Op_123456789101112_3 { + fourteen: multiply(by: 2) + } + - id: 4 + type: Operation + schema: c + operation: | + mutation Op_123456789101112_4 { + sixteen: add(num: 2) + } + - id: 5 + type: Operation + schema: b + operation: | + mutation Op_123456789101112_5 { + final: delete + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Root_Types_Shared_Root.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Root_Types_Shared_Root.yaml new file mode 100644 index 00000000000..b330d8d96fc --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/PlannerBehaviorTests.Root_Types_Shared_Root.yaml @@ -0,0 +1,64 @@ +operation: + - document: | + { + product { + id + name { + id + brand + model + } + category { + id + name + } + price { + id + amount + currency + } + } + } + hash: 123456789101112 + searchSpace: 3 + expandedNodes: 3 +nodes: + - id: 1 + type: Operation + schema: category + operation: | + query Op_123456789101112_1 { + product { + id + category { + id + name + } + } + } + - id: 2 + type: Operation + schema: name + operation: | + query Op_123456789101112_2 { + product { + name { + id + brand + model + } + } + } + - id: 3 + type: Operation + schema: price + operation: | + query Op_123456789101112_3 { + product { + price { + id + amount + currency + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Fed_Audit_Requires_With_Argument_Conflict.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Fed_Audit_Requires_With_Argument_Conflict.yaml new file mode 100644 index 00000000000..ffb4b2b1df5 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Fed_Audit_Requires_With_Argument_Conflict.yaml @@ -0,0 +1,79 @@ +operation: + - document: | + { + products { + upc + upc @fusion__requirement + name + shippingEstimate + shippingEstimateEUR + isExpensiveCategory + category @fusion__requirement { + averagePrice + } + priceEur @fusion__requirement + weight @fusion__requirement + price @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: b + operation: | + query Op_123456789101112_1 { + products { + upc + name + category { + averagePrice + } + priceEur + weight + price + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_upc: String! + $__fusion_2_averagePrice: Int + $__fusion_3_price: Int + $__fusion_3_weight: Int + $__fusion_4_price: Int + $__fusion_4_weight: Int + ) { + productByUpc(upc: $__fusion_1_upc) { + isExpensiveCategory(averagePrice: $__fusion_2_averagePrice) + shippingEstimateEUR(price: $__fusion_3_price, weight: $__fusion_3_weight) + shippingEstimate(price: $__fusion_4_price, weight: $__fusion_4_weight) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_1_upc + selectionMap: >- + upc + - name: __fusion_2_averagePrice + selectionMap: >- + category.averagePrice + - name: __fusion_3_price + selectionMap: >- + priceEur + - name: __fusion_3_weight + selectionMap: >- + weight + - name: __fusion_4_price + selectionMap: >- + price + - name: __fusion_4_weight + selectionMap: >- + weight + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Plain_Field_And_Requires_With_Args_That_Conflicts.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Plain_Field_And_Requires_With_Args_That_Conflicts.yaml new file mode 100644 index 00000000000..4afd921785f --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Plain_Field_And_Requires_With_Args_That_Conflicts.yaml @@ -0,0 +1,150 @@ +operation: + - document: | + { + test { + id + id @fusion__requirement + fieldWithRequiresAndArgs + anotherWithRequiresAndArgs + otherField(arg: 1) + otherField @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 9 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + test { + id + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_otherField: String! + $__fusion_2_id: ID! + ) { + testById(id: $__fusion_2_id) { + anotherWithRequiresAndArgs(otherField: $__fusion_1_otherField) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_1_otherField + selectionMap: >- + otherField + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: b + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + ) { + testById(id: $__fusion_3_id) { + otherField + id + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + ) { + testById(id: $__fusion_4_id) { + id + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: a + operation: | + query Op_123456789101112_5( + $__fusion_5_otherField: String! + $__fusion_6_id: ID! + ) { + testById(id: $__fusion_6_id) { + fieldWithRequiresAndArgs(otherField: $__fusion_5_otherField) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_5_otherField + selectionMap: >- + otherField + - name: __fusion_6_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 6 + - id: 6 + type: Operation + schema: b + operation: | + query Op_123456789101112_6( + $__fusion_7_id: ID! + ) { + testById(id: $__fusion_7_id) { + otherField + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_7_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 7 + type: Operation + schema: b + operation: | + query Op_123456789101112_7( + $__fusion_8_id: ID! + ) { + testById(id: $__fusion_8_id) { + otherField(arg: 1) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_8_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Plain_Field_And_Requires_With_Args_That_Does_Not_Conflicts_Should_Merge.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Plain_Field_And_Requires_With_Args_That_Does_Not_Conflicts_Should_Merge.yaml new file mode 100644 index 00000000000..c8f416db4eb --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Plain_Field_And_Requires_With_Args_That_Does_Not_Conflicts_Should_Merge.yaml @@ -0,0 +1,105 @@ +operation: + - document: | + { + test { + id + id @fusion__requirement + fieldWithRequiresAndArgs + otherField + otherField @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 7 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + test { + id + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_otherField: String! + $__fusion_2_id: ID! + ) { + testById(id: $__fusion_2_id) { + fieldWithRequiresAndArgs(otherField: $__fusion_1_otherField) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_1_otherField + selectionMap: >- + otherField + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: b + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + ) { + testById(id: $__fusion_3_id) { + otherField + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + ) { + testById(id: $__fusion_4_id) { + id + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: b + operation: | + query Op_123456789101112_5( + $__fusion_5_id: ID! + ) { + testById(id: $__fusion_5_id) { + otherField + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_5_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Requires_With_Args_That_Conflicts.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Requires_With_Args_That_Conflicts.yaml new file mode 100644 index 00000000000..0628acfa816 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Multiple_Requires_With_Args_That_Conflicts.yaml @@ -0,0 +1,130 @@ +operation: + - document: | + { + test { + id + id @fusion__requirement + fieldWithRequiresAndArgs + anotherWithRequiresAndArgs + otherField @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 8 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + test { + id + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_otherField: String! + $__fusion_2_id: ID! + ) { + testById(id: $__fusion_2_id) { + anotherWithRequiresAndArgs(otherField: $__fusion_1_otherField) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_1_otherField + selectionMap: >- + otherField + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: b + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + ) { + testById(id: $__fusion_3_id) { + otherField + id + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + ) { + testById(id: $__fusion_4_id) { + id + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: a + operation: | + query Op_123456789101112_5( + $__fusion_5_otherField: String! + $__fusion_6_id: ID! + ) { + testById(id: $__fusion_6_id) { + fieldWithRequiresAndArgs(otherField: $__fusion_5_otherField) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_5_otherField + selectionMap: >- + otherField + - name: __fusion_6_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 6 + - id: 6 + type: Operation + schema: b + operation: | + query Op_123456789101112_6( + $__fusion_7_id: ID! + ) { + testById(id: $__fusion_7_id) { + otherField + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_7_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires.yaml new file mode 100644 index 00000000000..0d24d96b2a4 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires.yaml @@ -0,0 +1,60 @@ +operation: + - document: | + { + feed { + author { + id + } + comments(limit: 1) { + id + } + comments @fusion__requirement { + authorId + } + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: c + operation: | + query Op_123456789101112_1 { + feed { + comments(limit: 1) { + id + } + comments { + authorId + } + id + } + } + - id: 2 + type: Operation + schema: d + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_commentAuthorIds: [ID] + ) { + postById(id: $__fusion_1_id) { + author(commentAuthorIds: $__fusion_2_commentAuthorIds) { + id + } + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_commentAuthorIds + selectionMap: >- + comments[authorId] + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires_With_Variable.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires_With_Variable.yaml new file mode 100644 index 00000000000..f24718d0e0c --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires_With_Variable.yaml @@ -0,0 +1,66 @@ +operation: + - document: | + query( + $limit: Int = 1 + ) { + feed { + author { + id + } + comments(limit: $limit) { + id + } + comments @fusion__requirement { + authorId + } + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: c + operation: | + query Op_123456789101112_1( + $limit: Int = 1 + ) { + feed { + comments(limit: $limit) { + id + } + comments { + authorId + } + id + } + } + forwardedVariables: + - limit + - id: 2 + type: Operation + schema: d + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_commentAuthorIds: [ID] + ) { + postById(id: $__fusion_1_id) { + author(commentAuthorIds: $__fusion_2_commentAuthorIds) { + id + } + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_commentAuthorIds + selectionMap: >- + comments[authorId] + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires_With_Variables_And_Fragments.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires_With_Variables_And_Fragments.yaml new file mode 100644 index 00000000000..f24718d0e0c --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_Arguments_Deeply_Nested_Requires_With_Variables_And_Fragments.yaml @@ -0,0 +1,66 @@ +operation: + - document: | + query( + $limit: Int = 1 + ) { + feed { + author { + id + } + comments(limit: $limit) { + id + } + comments @fusion__requirement { + authorId + } + id @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: c + operation: | + query Op_123456789101112_1( + $limit: Int = 1 + ) { + feed { + comments(limit: $limit) { + id + } + comments { + authorId + } + id + } + } + forwardedVariables: + - limit + - id: 2 + type: Operation + schema: d + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_commentAuthorIds: [ID] + ) { + postById(id: $__fusion_1_id) { + author(commentAuthorIds: $__fusion_2_commentAuthorIds) { + id + } + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_commentAuthorIds + selectionMap: >- + comments[authorId] + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_With_Arguments.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_With_Arguments.yaml new file mode 100644 index 00000000000..210e1db8b27 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Requires_With_Arguments.yaml @@ -0,0 +1,85 @@ +operation: + - document: | + { + feed { + author { + id + } + id @fusion__requirement + comments @fusion__requirement { + somethingElse + id @fusion__requirement + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + feed { + id + comments { + __typename + } + } + } + - id: 2 + type: Operation + schema: b + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_somethingElse: [String] + ) { + postById(id: $__fusion_1_id) { + author(somethingElse: $__fusion_2_somethingElse) { + id + } + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_somethingElse + selectionMap: >- + comments[somethingElse] + dependencies: + - id: 1 + - id: 3 + type: Operation + schema: c + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + ) { + commentById(id: $__fusion_3_id) { + somethingElse + } + } + source: $.commentById + target: $.feed.comments + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 4 + - id: 4 + type: Operation + schema: a + operation: | + query Op_123456789101112_4 { + feed { + comments @fusion__requirement { + id + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Simple_Requires_Arguments.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Simple_Requires_Arguments.yaml new file mode 100644 index 00000000000..2c85cc73a78 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementArgumentTests.Simple_Requires_Arguments.yaml @@ -0,0 +1,85 @@ +operation: + - document: | + { + test { + id + id @fusion__requirement + fieldWithRequiresAndArgs + otherField @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 6 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + test { + id + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_otherField: String! + $__fusion_2_id: ID! + ) { + testById(id: $__fusion_2_id) { + fieldWithRequiresAndArgs(otherField: $__fusion_1_otherField) + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_1_otherField + selectionMap: >- + otherField + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: b + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + ) { + testById(id: $__fusion_3_id) { + otherField + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + ) { + testById(id: $__fusion_4_id) { + id + } + } + source: $.testById + target: $.test + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Circular_1.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Circular_1.yaml new file mode 100644 index 00000000000..9fdeb567fd8 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Circular_1.yaml @@ -0,0 +1,50 @@ +operation: + - document: | + { + feed { + byNovice + id @fusion__requirement + author @fusion__requirement { + yearsOfExperience + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + feed { + id + author { + yearsOfExperience + } + } + } + - id: 2 + type: Operation + schema: b + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_yearsOfExperience: Int! + ) { + postById(id: $__fusion_1_id) { + byNovice(yearsOfExperience: $__fusion_2_yearsOfExperience) + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_yearsOfExperience + selectionMap: >- + author.yearsOfExperience + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Circular_2.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Circular_2.yaml new file mode 100644 index 00000000000..63b4a144be5 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Circular_2.yaml @@ -0,0 +1,94 @@ +operation: + - document: | + { + feed { + byExpert + id @fusion__requirement + byNovice @fusion__requirement + author @fusion__requirement { + yearsOfExperience + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 8 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + feed { + id + author { + yearsOfExperience + } + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_byNovice: Boolean! + $__fusion_2_id: ID! + ) { + postById(id: $__fusion_2_id) { + byExpert(byNovice: $__fusion_1_byNovice) + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_1_byNovice + selectionMap: >- + byNovice + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: b + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + $__fusion_4_yearsOfExperience: Int! + ) { + postById(id: $__fusion_3_id) { + byNovice(yearsOfExperience: $__fusion_4_yearsOfExperience) + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_3_id + selectionMap: >- + id + - name: __fusion_4_yearsOfExperience + selectionMap: >- + author.yearsOfExperience + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_5_id: ID! + ) { + postById(id: $__fusion_5_id) { + id + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_5_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_Many.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_Many.yaml new file mode 100644 index 00000000000..c96bfdfedcb --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_Many.yaml @@ -0,0 +1,272 @@ +operation: + - document: | + { + product { + id + id @fusion__requirement + price + price @fusion__requirement + hasDiscount + hasDiscount @fusion__requirement + isExpensive + isExpensive @fusion__requirement + isExpensiveWithDiscount + isExpensiveWithDiscount @fusion__requirement + canAfford + canAfford2 + canAffordWithDiscount + canAffordWithDiscount2 + } + } + hash: 123456789101112 + searchSpace: 21 + expandedNodes: 122 +nodes: + - id: 1 + type: Operation + schema: b + operation: | + query Op_123456789101112_1 { + product { + id + hasDiscount + } + } + - id: 2 + type: Operation + schema: a + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + ) { + productById(id: $__fusion_1_id) { + price + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_1_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: d + operation: | + query Op_123456789101112_4( + $__fusion_3_isExpensiveWithDiscount: Boolean! + $__fusion_4_id: ID! + ) { + productById(id: $__fusion_4_id) { + canAffordWithDiscount2(isExpensiveWithDiscount: $__fusion_3_isExpensiveWithDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_3_isExpensiveWithDiscount + selectionMap: >- + isExpensiveWithDiscount + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + - id: 5 + type: Operation + schema: c + operation: | + query Op_123456789101112_5( + $__fusion_5_id: ID! + $__fusion_6_hasDiscount: Boolean! + ) { + productById(id: $__fusion_5_id) { + isExpensiveWithDiscount(hasDiscount: $__fusion_6_hasDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_5_id + selectionMap: >- + id + - name: __fusion_6_hasDiscount + selectionMap: >- + hasDiscount + dependencies: + - id: 1 + - id: 6 + type: Operation + schema: d + operation: | + query Op_123456789101112_6( + $__fusion_7_isExpensiveWithDiscount: Boolean! + $__fusion_8_id: ID! + ) { + productById(id: $__fusion_8_id) { + canAffordWithDiscount(isExpensiveWithDiscount: $__fusion_7_isExpensiveWithDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_7_isExpensiveWithDiscount + selectionMap: >- + isExpensiveWithDiscount + - name: __fusion_8_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 7 + - id: 7 + type: Operation + schema: c + operation: | + query Op_123456789101112_7( + $__fusion_10_hasDiscount: Boolean! + $__fusion_9_id: ID! + ) { + productById(id: $__fusion_9_id) { + isExpensiveWithDiscount(hasDiscount: $__fusion_10_hasDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_10_hasDiscount + selectionMap: >- + hasDiscount + - name: __fusion_9_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 8 + type: Operation + schema: d + operation: | + query Op_123456789101112_8( + $__fusion_11_isExpensive: Boolean! + $__fusion_12_id: ID! + ) { + productById(id: $__fusion_12_id) { + canAfford2(isExpensive: $__fusion_11_isExpensive) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_11_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_12_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 9 + - id: 9 + type: Operation + schema: c + operation: | + query Op_123456789101112_9( + $__fusion_13_id: ID! + $__fusion_14_price: Float! + ) { + productById(id: $__fusion_13_id) { + isExpensive(price: $__fusion_14_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_13_id + selectionMap: >- + id + - name: __fusion_14_price + selectionMap: >- + price + dependencies: + - id: 1 + - id: 2 + - id: 10 + type: Operation + schema: d + operation: | + query Op_123456789101112_10( + $__fusion_15_isExpensive: Boolean! + $__fusion_16_id: ID! + ) { + productById(id: $__fusion_16_id) { + canAfford(isExpensive: $__fusion_15_isExpensive) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_15_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_16_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 11 + - id: 11 + type: Operation + schema: c + operation: | + query Op_123456789101112_11( + $__fusion_17_id: ID! + $__fusion_18_price: Float! + ) { + productById(id: $__fusion_17_id) { + isExpensive(price: $__fusion_18_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_17_id + selectionMap: >- + id + - name: __fusion_18_price + selectionMap: >- + price + dependencies: + - id: 1 + - id: 2 + - id: 12 + type: Operation + schema: c + operation: | + query Op_123456789101112_12( + $__fusion_19_id: ID! + $__fusion_20_hasDiscount: Boolean! + $__fusion_21_price: Float! + ) { + productById(id: $__fusion_19_id) { + isExpensiveWithDiscount(hasDiscount: $__fusion_20_hasDiscount) + isExpensive(price: $__fusion_21_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_19_id + selectionMap: >- + id + - name: __fusion_20_hasDiscount + selectionMap: >- + hasDiscount + - name: __fusion_21_price + selectionMap: >- + price + dependencies: + - id: 1 + - id: 2 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_One.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_One.yaml new file mode 100644 index 00000000000..baee5a6b852 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_One.yaml @@ -0,0 +1,71 @@ +operation: + - document: | + { + product { + canAffordWithDiscount + id @fusion__requirement + isExpensiveWithDiscount @fusion__requirement + hasDiscount @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 8 +nodes: + - id: 1 + type: Operation + schema: b + operation: | + query Op_123456789101112_1 { + product { + id + hasDiscount + } + } + - id: 3 + type: Operation + schema: d + operation: | + query Op_123456789101112_3( + $__fusion_2_isExpensiveWithDiscount: Boolean! + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + canAffordWithDiscount(isExpensiveWithDiscount: $__fusion_2_isExpensiveWithDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_2_isExpensiveWithDiscount + selectionMap: >- + isExpensiveWithDiscount + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + - id: 4 + type: Operation + schema: c + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + $__fusion_5_hasDiscount: Boolean! + ) { + productById(id: $__fusion_4_id) { + isExpensiveWithDiscount(hasDiscount: $__fusion_5_hasDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_4_id + selectionMap: >- + id + - name: __fusion_5_hasDiscount + selectionMap: >- + hasDiscount + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_Two_Fields_Same_Requirement_Different_Order.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_Two_Fields_Same_Requirement_Different_Order.yaml new file mode 100644 index 00000000000..4efd27f308f --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementChainTests.Requires_Requires_Two_Fields_Same_Requirement_Different_Order.yaml @@ -0,0 +1,178 @@ +operation: + - document: | + { + product { + canAffordWithAndWithoutDiscount + canAffordWithAndWithoutDiscount2 + id @fusion__requirement + isExpensive @fusion__requirement + isExpensiveWithDiscount @fusion__requirement + hasDiscount @fusion__requirement + price @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 3 + expandedNodes: 22 +nodes: + - id: 1 + type: Operation + schema: b + operation: | + query Op_123456789101112_1 { + product { + id + hasDiscount + } + } + - id: 3 + type: Operation + schema: d + operation: | + query Op_123456789101112_3( + $__fusion_2_isExpensive: Boolean! + $__fusion_2_isExpensiveWithDiscount: Boolean! + $__fusion_3_id: ID! + ) { + productById(id: $__fusion_3_id) { + canAffordWithAndWithoutDiscount2(isExpensive: $__fusion_2_isExpensive, isExpensiveWithDiscount: $__fusion_2_isExpensiveWithDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_2_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_2_isExpensiveWithDiscount + selectionMap: >- + isExpensiveWithDiscount + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + - id: 4 + type: Operation + schema: c + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + $__fusion_5_hasDiscount: Boolean! + ) { + productById(id: $__fusion_4_id) { + isExpensiveWithDiscount(hasDiscount: $__fusion_5_hasDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_4_id + selectionMap: >- + id + - name: __fusion_5_hasDiscount + selectionMap: >- + hasDiscount + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: c + operation: | + query Op_123456789101112_5( + $__fusion_6_price: Float! + $__fusion_7_id: ID! + ) { + productById(id: $__fusion_7_id) { + isExpensive(price: $__fusion_6_price) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_6_price + selectionMap: >- + price + - name: __fusion_7_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 6 + - id: 6 + type: Operation + schema: a + operation: | + query Op_123456789101112_6( + $__fusion_8_id: ID! + ) { + productById(id: $__fusion_8_id) { + price + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_8_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 7 + type: Operation + schema: d + operation: | + query Op_123456789101112_7( + $__fusion_10_id: ID! + $__fusion_9_isExpensive: Boolean! + $__fusion_9_isExpensiveWithDiscount: Boolean! + ) { + productById(id: $__fusion_10_id) { + canAffordWithAndWithoutDiscount(isExpensiveWithDiscount: $__fusion_9_isExpensiveWithDiscount, isExpensive: $__fusion_9_isExpensive) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_10_id + selectionMap: >- + id + - name: __fusion_9_isExpensive + selectionMap: >- + isExpensive + - name: __fusion_9_isExpensiveWithDiscount + selectionMap: >- + isExpensiveWithDiscount + dependencies: + - id: 1 + - id: 8 + - id: 8 + type: Operation + schema: c + operation: | + query Op_123456789101112_8( + $__fusion_11_id: ID! + $__fusion_12_price: Float! + $__fusion_13_hasDiscount: Boolean! + ) { + productById(id: $__fusion_11_id) { + isExpensive(price: $__fusion_12_price) + isExpensiveWithDiscount(hasDiscount: $__fusion_13_hasDiscount) + } + } + source: $.productById + target: $.product + requirements: + - name: __fusion_11_id + selectionMap: >- + id + - name: __fusion_12_price + selectionMap: >- + price + - name: __fusion_13_hasDiscount + selectionMap: >- + hasDiscount + dependencies: + - id: 1 + - id: 6 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Deep_Requires.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Deep_Requires.yaml new file mode 100644 index 00000000000..5462683af3b --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Deep_Requires.yaml @@ -0,0 +1,93 @@ +operation: + - document: | + { + feed { + author { + id + } + id @fusion__requirement + comments @fusion__requirement { + authorId + id @fusion__requirement + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 7 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + feed { + id + } + } + - id: 3 + type: Operation + schema: d + operation: | + query Op_123456789101112_3( + $__fusion_2_commentAuthorIds: [ID] + $__fusion_3_id: ID! + ) { + postById(id: $__fusion_3_id) { + author(commentAuthorIds: $__fusion_2_commentAuthorIds) { + id + } + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_2_commentAuthorIds + selectionMap: >- + comments[authorId] + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + - id: 4 + type: Operation + schema: b + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + ) { + postById(id: $__fusion_4_id) { + comments { + id + } + } + } + source: $.postById + target: $.feed + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: c + operation: | + query Op_123456789101112_5( + $__fusion_5_id: ID! + ) { + commentById(id: $__fusion_5_id) { + authorId + } + } + source: $.commentById + target: $.feed.comments + requirements: + - name: __fusion_5_id + selectionMap: >- + id + dependencies: + - id: 4 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Keys_Mashup.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Keys_Mashup.yaml new file mode 100644 index 00000000000..ab0c53eedbf --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Keys_Mashup.yaml @@ -0,0 +1,127 @@ +operation: + - document: | + { + b { + id + a { + id + id @fusion__requirement + name + name @fusion__requirement + nameInB + compositeId @fusion__requirement { + two + three + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 7 +nodes: + - id: 1 + type: Operation + schema: b + operation: | + query Op_123456789101112_1 { + b { + id + a { + id + } + } + } + - id: 2 + type: Operation + schema: b + operation: | + query Op_123456789101112_2( + $__fusion_1_name: String! + $__fusion_2_compositeIdThree: ID! + $__fusion_2_compositeIdTwo: ID! + $__fusion_2_id: ID! + ) { + aByIdAndCompositeId(id: $__fusion_2_id, compositeIdTwo: $__fusion_2_compositeIdTwo, compositeIdThree: $__fusion_2_compositeIdThree) { + nameInB(name: $__fusion_1_name) + } + } + source: $.aByIdAndCompositeId + target: $.b.a + requirements: + - name: __fusion_1_name + selectionMap: >- + name + - name: __fusion_2_compositeIdThree + selectionMap: >- + compositeId.three + - name: __fusion_2_compositeIdTwo + selectionMap: >- + compositeId.two + - name: __fusion_2_id + selectionMap: >- + id + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: a + operation: | + query Op_123456789101112_3( + $__fusion_3_id: ID! + ) { + aById(id: $__fusion_3_id) { + name + } + } + source: $.aById + target: $.b.a + requirements: + - name: __fusion_3_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: a + operation: | + query Op_123456789101112_4( + $__fusion_4_id: ID! + ) { + aById(id: $__fusion_4_id) { + id + compositeId { + two + three + } + } + } + source: $.aById + target: $.b.a + requirements: + - name: __fusion_4_id + selectionMap: >- + id + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: a + operation: | + query Op_123456789101112_5( + $__fusion_5_id: ID! + ) { + aById(id: $__fusion_5_id) { + name + } + } + source: $.aById + target: $.b.a + requirements: + - name: __fusion_5_id + selectionMap: >- + id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Requires_With_Fragments_On_Interfaces.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Requires_With_Fragments_On_Interfaces.yaml new file mode 100644 index 00000000000..9fdfbef000b --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Requires_With_Fragments_On_Interfaces.yaml @@ -0,0 +1,84 @@ +operation: + - document: | + { + userFromA { + permissions + id @fusion__requirement + profile @fusion__requirement { + __typename @fusion__requirement + displayName + ... on AdminAccount { + accountType + adminLevel + } + ... on GuestAccount { + accountType + guestToken + } + } + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: a + operation: | + query Op_123456789101112_1 { + userFromA { + id + profile { + __typename + displayName + ... on AdminAccount { + accountType + } + ... on GuestAccount { + accountType + } + ... on AdminAccount { + adminLevel + } + ... on GuestAccount { + guestToken + } + } + } + } + - id: 2 + type: Operation + schema: b + operation: | + query Op_123456789101112_2( + $__fusion_1_id: ID! + $__fusion_2_accountType: String + $__fusion_2_adminLevel: String + $__fusion_2_displayName: String! + $__fusion_2_guestToken: String + ) { + userById(id: $__fusion_1_id) { + permissions(displayName: $__fusion_2_displayName, accountType: $__fusion_2_accountType, adminLevel: $__fusion_2_adminLevel, guestToken: $__fusion_2_guestToken) + } + } + source: $.userById + target: $.userFromA + requirements: + - name: __fusion_1_id + selectionMap: >- + id + - name: __fusion_2_accountType + selectionMap: >- + profile.accountType | profile.accountType + - name: __fusion_2_adminLevel + selectionMap: >- + profile.adminLevel + - name: __fusion_2_displayName + selectionMap: >- + profile.displayName + - name: __fusion_2_guestToken + selectionMap: >- + profile.guestToken + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Simple_Requires.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Simple_Requires.yaml new file mode 100644 index 00000000000..ecc1a9e3422 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Simple_Requires.yaml @@ -0,0 +1,52 @@ +operation: + - document: | + { + products { + shippingEstimate + upc @fusion__requirement + price @fusion__requirement + weight @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: products + operation: | + query Op_123456789101112_1 { + products { + upc + price + weight + } + } + - id: 2 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_2( + $__fusion_1_upc: String! + $__fusion_2_price: Int + $__fusion_2_weight: Int + ) { + productByUpc(upc: $__fusion_1_upc) { + shippingEstimate(price: $__fusion_2_price, weight: $__fusion_2_weight) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_1_upc + selectionMap: >- + upc + - name: __fusion_2_price + selectionMap: >- + price + - name: __fusion_2_weight + selectionMap: >- + weight + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Simplest_Requires.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Simplest_Requires.yaml new file mode 100644 index 00000000000..87bb700aaea --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Simplest_Requires.yaml @@ -0,0 +1,46 @@ +operation: + - document: | + { + products { + isExpensive + upc @fusion__requirement + price @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: products + operation: | + query Op_123456789101112_1 { + products { + upc + price + } + } + - id: 2 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_2( + $__fusion_1_upc: String! + $__fusion_2_price: Int + ) { + productByUpc(upc: $__fusion_1_upc) { + isExpensive(price: $__fusion_2_price) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_1_upc + selectionMap: >- + upc + - name: __fusion_2_price + selectionMap: >- + price + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Two_Fields_Same_Subgraph_Same_Requirement.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Two_Fields_Same_Subgraph_Same_Requirement.yaml new file mode 100644 index 00000000000..56ca5545f0e --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Two_Fields_Same_Subgraph_Same_Requirement.yaml @@ -0,0 +1,62 @@ +operation: + - document: | + { + products { + shippingEstimate + shippingEstimate2 + upc @fusion__requirement + price @fusion__requirement + weight @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 2 +nodes: + - id: 1 + type: Operation + schema: products + operation: | + query Op_123456789101112_1 { + products { + upc + price + weight + } + } + - id: 2 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_2( + $__fusion_1_upc: String! + $__fusion_2_price: Int + $__fusion_2_weight: Int + $__fusion_3_price: Int + $__fusion_3_weight: Int + ) { + productByUpc(upc: $__fusion_1_upc) { + shippingEstimate2(price: $__fusion_2_price, weight: $__fusion_2_weight) + shippingEstimate(price: $__fusion_3_price, weight: $__fusion_3_weight) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_1_upc + selectionMap: >- + upc + - name: __fusion_2_price + selectionMap: >- + price + - name: __fusion_2_weight + selectionMap: >- + weight + - name: __fusion_3_price + selectionMap: >- + price + - name: __fusion_3_weight + selectionMap: >- + weight + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Two_Same_Service_Calls_With_Args_Conflicts.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Two_Same_Service_Calls_With_Args_Conflicts.yaml new file mode 100644 index 00000000000..aa4cd013e7f --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/RequirementParityTests.Two_Same_Service_Calls_With_Args_Conflicts.yaml @@ -0,0 +1,149 @@ +operation: + - document: | + { + products { + isExpensive + reducedPrice + price(withDiscount: true) + price @fusion__requirement + upc @fusion__requirement + } + } + hash: 123456789101112 + searchSpace: 1 + expandedNodes: 9 +nodes: + - id: 1 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_1 { + products { + upc + } + } + - id: 2 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_2( + $__fusion_1_price: Int + $__fusion_2_upc: String! + ) { + productByUpc(upc: $__fusion_2_upc) { + reducedPrice(price: $__fusion_1_price) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_1_price + selectionMap: >- + price + - name: __fusion_2_upc + selectionMap: >- + upc + dependencies: + - id: 3 + - id: 4 + - id: 3 + type: Operation + schema: products + operation: | + query Op_123456789101112_3( + $__fusion_3_upc: String! + ) { + productByUpc(upc: $__fusion_3_upc) { + price + upc + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_3_upc + selectionMap: >- + upc + dependencies: + - id: 1 + - id: 4 + type: Operation + schema: products + operation: | + query Op_123456789101112_4( + $__fusion_4_upc: String! + ) { + productByUpc(upc: $__fusion_4_upc) { + upc + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_4_upc + selectionMap: >- + upc + dependencies: + - id: 1 + - id: 5 + type: Operation + schema: inventory + operation: | + query Op_123456789101112_5( + $__fusion_5_price: Int + $__fusion_6_upc: String! + ) { + productByUpc(upc: $__fusion_6_upc) { + isExpensive(price: $__fusion_5_price) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_5_price + selectionMap: >- + price + - name: __fusion_6_upc + selectionMap: >- + upc + dependencies: + - id: 3 + - id: 6 + - id: 6 + type: Operation + schema: products + operation: | + query Op_123456789101112_6( + $__fusion_7_upc: String! + ) { + productByUpc(upc: $__fusion_7_upc) { + price + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_7_upc + selectionMap: >- + upc + dependencies: + - id: 1 + - id: 7 + type: Operation + schema: products + operation: | + query Op_123456789101112_7( + $__fusion_8_upc: String! + ) { + productByUpc(upc: $__fusion_8_upc) { + price(withDiscount: true) + } + } + source: $.productByUpc + target: $.products + requirements: + - name: __fusion_8_upc + selectionMap: >- + upc + dependencies: + - id: 1