From 91047904f76f3b7960445f66f62dedeff4a5cc92 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 20 Sep 2024 16:45:20 +0200 Subject: [PATCH 1/8] some tests around variables usage --- .../tests/samples/basic/variables/README.md | 3 + .../basic/variables/configuration.yaml | 4 + .../tests/samples/basic/variables/plan.json | 77 +++++++++++++++ .../basic/variables/supergraph.graphql | 94 +++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 apollo-router/tests/samples/basic/variables/README.md create mode 100644 apollo-router/tests/samples/basic/variables/configuration.yaml create mode 100644 apollo-router/tests/samples/basic/variables/plan.json create mode 100644 apollo-router/tests/samples/basic/variables/supergraph.graphql diff --git a/apollo-router/tests/samples/basic/variables/README.md b/apollo-router/tests/samples/basic/variables/README.md new file mode 100644 index 0000000000..48fa96c726 --- /dev/null +++ b/apollo-router/tests/samples/basic/variables/README.md @@ -0,0 +1,3 @@ +# Variables + +Check variable usage in queries. \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/variables/configuration.yaml b/apollo-router/tests/samples/basic/variables/configuration.yaml new file mode 100644 index 0000000000..f7ed04641e --- /dev/null +++ b/apollo-router/tests/samples/basic/variables/configuration.yaml @@ -0,0 +1,4 @@ +override_subgraph_url: + products: http://localhost:4005 +include_subgraph_errors: + all: true diff --git a/apollo-router/tests/samples/basic/variables/plan.json b/apollo-router/tests/samples/basic/variables/plan.json new file mode 100644 index 0000000000..d433345ec8 --- /dev/null +++ b/apollo-router/tests/samples/basic/variables/plan.json @@ -0,0 +1,77 @@ +{ + "actions": [ + { + "type": "Start", + "schema_path": "./supergraph.graphql", + "configuration_path": "./configuration.yaml", + "subgraphs": { + "products": { + "requests": [ + { + "request": { + "body": { + "query": "query Variables__products__0($a:String$b:String){a(arg:$a)aliased:a(arg:$b)}", + "operationName": "Variables__products__0" + } + }, + "response": { + "body": { + "data": { + "a": "a", + "aliased": "a" + } + } + } + }, + { + "request": { + "body": { + "query": "query Variables($a: String, $b: String, $c: String, $d: String) { d(arg: [\"a\", $a, \"b\"]) e(arg: { a: $b}) ...F } fragment F on Query { aa: d(arg: [\"a\", $c, \"b\"]) ab: e(arg: { a: $d})) }" + } + }, + "response": { + "body": { + "data": { + "d": "d", + "e": "e", + "aa": "d", + "ab": "e" + } + } + } + } + ] + } + } + }, + { + "type": "Request", + "request": { + "query": "query Variables($a: String, $b: String) { a(arg: $a) ...F } fragment F on Query { aliased: a(arg: $b) }" + }, + "expected_response": { + "data": { + "a": "a", + "aliased": "a" + } + } + }, + { + "type": "Request", + "request": { + "query": "query Variables($a: String, $b: String, $c: String, $d: String) { d(arg: [\"a\", $a, \"b\"]) e(arg: { a: $b}) ...F } fragment F on Query { aa: d(arg: [\"a\", $c, \"b\"]) ab: e(arg: { a: $d}) }" + }, + "expected_response": { + "data": { + "d": "d", + "e": "e", + "aa": "d", + "ab": "e" + } + } + }, + { + "type": "Stop" + } + ] +} \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/variables/supergraph.graphql b/apollo-router/tests/samples/basic/variables/supergraph.graphql new file mode 100644 index 0000000000..e66e0bd402 --- /dev/null +++ b/apollo-router/tests/samples/basic/variables/supergraph.graphql @@ -0,0 +1,94 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) + @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) { + query: Query +} + +directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +directive @tag( + name: String! +) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA + +scalar join__FieldSet + +enum join__Graph { + ACCOUNTS + @join__graph(name: "accounts", url: "https://accounts.demo.starstuff.dev/") + INVENTORY + @join__graph( + name: "inventory" + url: "https://inventory.demo.starstuff.dev/" + ) + PRODUCTS + @join__graph(name: "products", url: "https://products.demo.starstuff.dev/") + REVIEWS + @join__graph(name: "reviews", url: "https://reviews.demo.starstuff.dev/") +} + +scalar link__Import + +enum link__Purpose { + SECURITY + EXECUTION +} + +type Query +@join__type(graph: PRODUCTS) { + a(arg: String): String @join__field(graph: PRODUCTS) + b: Obj @join__field(graph: PRODUCTS) + c: Int @join__field(graph: PRODUCTS) + d(arg: [String]): String @join__field(graph: PRODUCTS) + e(arg: Inp): String @join__field(graph: PRODUCTS) + f(arg: [[String]]): String @join__field(graph: PRODUCTS) + g(arg: [Inp]): String @join__field(graph: PRODUCTS) +} + +input Inp @join__type(graph: PRODUCTS) { + a: String + b: String + c: [String] +} +type Obj @join__type(graph: PRODUCTS) { + a: String @join__field(graph: PRODUCTS) +} From 50e9f1e050d1a8e9ecff4546b2963fa8925d5178 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 20 Sep 2024 16:45:39 +0200 Subject: [PATCH 2/8] test the inaccessible directive --- .../samples/basic/inaccessible/README.md | 3 + .../basic/inaccessible/configuration.yaml | 4 + .../samples/basic/inaccessible/plan.json | 144 ++++++++++++++++++ .../basic/inaccessible/supergraph.graphql | 103 +++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 apollo-router/tests/samples/basic/inaccessible/README.md create mode 100644 apollo-router/tests/samples/basic/inaccessible/configuration.yaml create mode 100644 apollo-router/tests/samples/basic/inaccessible/plan.json create mode 100644 apollo-router/tests/samples/basic/inaccessible/supergraph.graphql diff --git a/apollo-router/tests/samples/basic/inaccessible/README.md b/apollo-router/tests/samples/basic/inaccessible/README.md new file mode 100644 index 0000000000..fa1ae57775 --- /dev/null +++ b/apollo-router/tests/samples/basic/inaccessible/README.md @@ -0,0 +1,3 @@ +# Inaccessible + +Test the `inaccessible` directive. \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/inaccessible/configuration.yaml b/apollo-router/tests/samples/basic/inaccessible/configuration.yaml new file mode 100644 index 0000000000..f7ed04641e --- /dev/null +++ b/apollo-router/tests/samples/basic/inaccessible/configuration.yaml @@ -0,0 +1,4 @@ +override_subgraph_url: + products: http://localhost:4005 +include_subgraph_errors: + all: true diff --git a/apollo-router/tests/samples/basic/inaccessible/plan.json b/apollo-router/tests/samples/basic/inaccessible/plan.json new file mode 100644 index 0000000000..a6ebc82cd5 --- /dev/null +++ b/apollo-router/tests/samples/basic/inaccessible/plan.json @@ -0,0 +1,144 @@ +{ + "actions": [ + { + "type": "Start", + "schema_path": "./supergraph.graphql", + "configuration_path": "./configuration.yaml", + "subgraphs": { + "accounts": { + "requests": [ + { + "request": { + "body": { + "query": "{me{name}}" + } + }, + "response": { + "body": { + "data": { + "me": { + "name": "test" + } + } + } + } + }, + { + "request": { + "body": { + "query": "{me{__typename id name}}" + } + }, + "response": { + "body": { + "data": { + "me": { + "__typename": "User", + "id": "1", + "name": "test" + } + } + } + } + } + ] + }, + "reviews": { + "requests": [ + { + "request": { + "body": { + "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}", + "variables": { + "representations": [ + { + "__typename": "User", + "id": "1" + } + ] + } + } + }, + "response": { + "body": { + "data": { + "_entities": [ + { + "reviews": [ + { + "body": "test" + } + ] + } + ] + } + } + } + } + ] + } + } + }, + { + "type": "Request", + "request": { + "query": "{ topProducts { upc name } }" + }, + "expected_response": { + "errors": [ + { + "message": "Cannot query field \"upc\" on type \"Product\".", + "locations": [ + { + "line": 1, + "column": 17 + } + ], + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED" + } + } + ] + } + }, + { + "type": "Request", + "request": { + "query": "{ me { name reviews { body } } }" + }, + "expected_response": { + "data": { + "me": { + "name": "test", + "reviews": [ + { + "body": "test" + } + ] + } + } + } + }, + { + "type": "Request", + "request": { + "query": "query Invalid { me { name { reviews { body } } } }" + }, + "expected_response": { + "data": { + "me": { + "name": "test", + "reviews": [ + { + "body": "test" + } + ] + } + } + } + }, + { + "type": "Stop" + } + ] +} \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/inaccessible/supergraph.graphql b/apollo-router/tests/samples/basic/inaccessible/supergraph.graphql new file mode 100644 index 0000000000..d61495aa37 --- /dev/null +++ b/apollo-router/tests/samples/basic/inaccessible/supergraph.graphql @@ -0,0 +1,103 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) + @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) { + query: Query +} + +directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +directive @tag( + name: String! +) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA + +scalar join__FieldSet +scalar link__Import + +enum join__Graph { + ACCOUNTS + @join__graph(name: "accounts", url: "https://accounts.demo.starstuff.dev/") + PRODUCTS + @join__graph(name: "products", url: "https://products.demo.starstuff.dev/") + REVIEWS + @join__graph(name: "reviews", url: "https://reviews.demo.starstuff.dev/") +} + +enum link__Purpose { + SECURITY + EXECUTION +} + +type Product + @join__type(graph: PRODUCTS, key: "upc") + @join__type(graph: REVIEWS, key: "upc") { + name: String @join__field(graph: PRODUCTS) + reviews: [Review] @join__field(graph: REVIEWS) + reviewsForAuthor(authorID: ID!): [Review] @join__field(graph: REVIEWS) + upc: String! @inaccessible +} + +type Query + @join__type(graph: ACCOUNTS) + @join__type(graph: PRODUCTS) + @join__type(graph: REVIEWS) { + me: User @join__field(graph: ACCOUNTS) + topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCTS) +} + +type Review @join__type(graph: REVIEWS, key: "id") { + id: ID! @inaccessible + author: User @join__field(graph: REVIEWS, provides: "username") + body: String @join__field(graph: REVIEWS) + product: Product @join__field(graph: REVIEWS) +} + +type User + @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: REVIEWS, key: "id") { + id: ID! @inaccessible + name: String @join__field(graph: ACCOUNTS) + username: String + @join__field(graph: ACCOUNTS) + @join__field(graph: REVIEWS, external: true) @inaccessible + reviews: [Review] @join__field(graph: REVIEWS) +} From ff9ded80d45aa42112755f879f3f4ea7eef16dd8 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 20 Sep 2024 16:46:03 +0200 Subject: [PATCH 3/8] test the requires directive --- .../tests/samples/basic/requires/README.md | 4 + .../samples/basic/requires/configuration.yaml | 7 + .../tests/samples/basic/requires/plan.json | 130 ++++++++++++++++++ .../samples/basic/requires/supergraph.graphql | 120 ++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 apollo-router/tests/samples/basic/requires/README.md create mode 100644 apollo-router/tests/samples/basic/requires/configuration.yaml create mode 100644 apollo-router/tests/samples/basic/requires/plan.json create mode 100644 apollo-router/tests/samples/basic/requires/supergraph.graphql diff --git a/apollo-router/tests/samples/basic/requires/README.md b/apollo-router/tests/samples/basic/requires/README.md new file mode 100644 index 0000000000..2484b7cb3c --- /dev/null +++ b/apollo-router/tests/samples/basic/requires/README.md @@ -0,0 +1,4 @@ + +# Requires + +Test the `requires` directive. \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/requires/configuration.yaml b/apollo-router/tests/samples/basic/requires/configuration.yaml new file mode 100644 index 0000000000..d5c60afffa --- /dev/null +++ b/apollo-router/tests/samples/basic/requires/configuration.yaml @@ -0,0 +1,7 @@ +override_subgraph_url: + products: http://localhost:4005 +include_subgraph_errors: + all: true + +plugins: + experimental.expose_query_plan: true \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/requires/plan.json b/apollo-router/tests/samples/basic/requires/plan.json new file mode 100644 index 0000000000..076a981b1d --- /dev/null +++ b/apollo-router/tests/samples/basic/requires/plan.json @@ -0,0 +1,130 @@ +{ + "actions": [ + { + "type": "Start", + "schema_path": "./supergraph.graphql", + "configuration_path": "./configuration.yaml", + "subgraphs": { + "accounts": { + "requests": [ + { + "request": { + "body": { + "query": "{me{__typename id name username}}" + } + }, + "response": { + "body": { + "data": { + "me": { + "__typename": "User", + "id": "1", + "name": "usertest", + "username": "usertest" + } + } + } + } + } + ] + }, + "reviews": { + "requests": [ + { + "request": { + "body": { + "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{author{username}body product{__typename upc}}}}}", + "variables": { + "representations": [ + { + "__typename": "User", + "id": "1", + "name": "usertest", + "username": "usertest" + } + ] + } + } + }, + "response": { + "body": { + "data": { + "_entities": [ + { + "reviews": [ + { + "author": { + "username": "usertest" + }, + "body": "review", + "product": { + "__typename": "Product", + "upc": "1" + } + } + ] + } + ] + } + } + } + } + ] + }, + "products": { + "requests": [ + { + "request": { + "body": { + "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on Product{name}}}", + "variables": { + "representations": [ + { + "__typename": "Product", + "upc": "1" + } + ] + } + } + }, + "response": { + "body": { + "data": { + "_entities": [ + { + "name": "product test" + } + ] + } + } + } + } + ] + } + } + }, + { + "type": "Request", + "request": { + "query": "{ me { reviews { body product { name } } } }" + }, + "expected_response": { + "data": { + "me": { + "reviews": [ + { + "body": "review", + "product": { + "name": "product test" + } + } + ] + } + } + } + }, + { + "type": "Stop" + } + ] +} \ No newline at end of file diff --git a/apollo-router/tests/samples/basic/requires/supergraph.graphql b/apollo-router/tests/samples/basic/requires/supergraph.graphql new file mode 100644 index 0000000000..70288653db --- /dev/null +++ b/apollo-router/tests/samples/basic/requires/supergraph.graphql @@ -0,0 +1,120 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) + @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) { + query: Query + mutation: Mutation +} + +directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +directive @tag( + name: String! +) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA + +scalar join__FieldSet +scalar link__Import + +enum join__Graph { + ACCOUNTS + @join__graph(name: "accounts", url: "https://accounts.demo.starstuff.dev/") + INVENTORY + @join__graph( + name: "inventory" + url: "https://inventory.demo.starstuff.dev/" + ) + PRODUCTS + @join__graph(name: "products", url: "https://products.demo.starstuff.dev/") + REVIEWS + @join__graph(name: "reviews", url: "https://reviews.demo.starstuff.dev/") +} + +enum link__Purpose { + SECURITY + EXECUTION +} + +type Mutation @join__type(graph: PRODUCTS) @join__type(graph: REVIEWS) { + createProduct(upc: ID!, name: String): Product @join__field(graph: PRODUCTS) + createReview(upc: ID!, id: ID!, body: String): Review + @join__field(graph: REVIEWS) +} + +type Product + @join__type(graph: INVENTORY, key: "upc") + @join__type(graph: PRODUCTS, key: "upc") + @join__type(graph: REVIEWS, key: "upc") { + + name: String @join__field(graph: PRODUCTS) + reviews: [Review] @join__field(graph: REVIEWS) + reviewsForAuthor(authorID: ID!): [Review] @join__field(graph: REVIEWS) + upc: String! +} + +type Query + @join__type(graph: ACCOUNTS) + @join__type(graph: INVENTORY) + @join__type(graph: PRODUCTS) + @join__type(graph: REVIEWS) { + me: User @join__field(graph: ACCOUNTS) + topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCTS) +} + +type Review @join__type(graph: REVIEWS, key: "id") { + id: ID! + author: User @join__field(graph: REVIEWS, provides: "username", requires: "product {upc}") + body: String @join__field(graph: REVIEWS) + product: Product @join__field(graph: REVIEWS, requires: "author { ... on User { id username } }") +} + +type User + @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: REVIEWS, key: "id") { + id: ID! + name: String + @join__field(graph: ACCOUNTS) + @join__field(graph: REVIEWS, external: true) + username: String + @join__field(graph: ACCOUNTS) + @join__field(graph: REVIEWS, external: true) + reviews: [Review] @join__field(graph: REVIEWS, , requires: "name username") +} From 245e467f021522c6a1205dd3b5c7156666e6e873 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 20 Sep 2024 17:45:41 +0200 Subject: [PATCH 4/8] update requires test --- .../tests/samples/basic/requires/plan.json | 64 +++++------ .../samples/basic/requires/supergraph.graphql | 105 ++++++++++++------ 2 files changed, 101 insertions(+), 68 deletions(-) diff --git a/apollo-router/tests/samples/basic/requires/plan.json b/apollo-router/tests/samples/basic/requires/plan.json index 076a981b1d..536466f9a5 100644 --- a/apollo-router/tests/samples/basic/requires/plan.json +++ b/apollo-router/tests/samples/basic/requires/plan.json @@ -10,17 +10,17 @@ { "request": { "body": { - "query": "{me{__typename id name username}}" + "query": "query TestA__accounts__0{a{__typename id a}}", + "operationName": "TestA__accounts__0" } }, "response": { "body": { "data": { - "me": { - "__typename": "User", + "a": { + "__typename": "A", "id": "1", - "name": "usertest", - "username": "usertest" + "a": "a" } } } @@ -33,14 +33,14 @@ { "request": { "body": { - "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{author{username}body product{__typename upc}}}}}", + "query": "query TestA__reviews__1($representations:[_Any!]!){_entities(representations:$representations){...on A{b{b}}}}", + "operationName": "TestA__reviews__1", "variables": { "representations": [ { - "__typename": "User", - "id": "1", - "name": "usertest", - "username": "usertest" + "__typename": "A", + "a": "a", + "id": "1" } ] } @@ -51,18 +51,9 @@ "data": { "_entities": [ { - "reviews": [ - { - "author": { - "username": "usertest" - }, - "body": "review", - "product": { - "__typename": "Product", - "upc": "1" - } - } - ] + "b": { + "b": "b" + } } ] } @@ -76,12 +67,16 @@ { "request": { "body": { - "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on Product{name}}}", + "query": "query TestA__products__2($representations:[_Any!]!){_entities(representations:$representations){...on A{c{c}}}}", + "operationName": "TestA__products__2", "variables": { "representations": [ { - "__typename": "Product", - "upc": "1" + "__typename": "A", + "b": { + "b": "b" + }, + "id": "1" } ] } @@ -92,7 +87,9 @@ "data": { "_entities": [ { - "name": "product test" + "c": { + "c": "c" + } } ] } @@ -106,19 +103,14 @@ { "type": "Request", "request": { - "query": "{ me { reviews { body product { name } } } }" + "query": "query TestA { a { c { c } } }" }, "expected_response": { "data": { - "me": { - "reviews": [ - { - "body": "review", - "product": { - "name": "product test" - } - } - ] + "a": { + "c": { + "c": "c" + } } } } diff --git a/apollo-router/tests/samples/basic/requires/supergraph.graphql b/apollo-router/tests/samples/basic/requires/supergraph.graphql index 70288653db..6b88b71040 100644 --- a/apollo-router/tests/samples/basic/requires/supergraph.graphql +++ b/apollo-router/tests/samples/basic/requires/supergraph.graphql @@ -3,7 +3,6 @@ schema @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) { query: Query - mutation: Mutation } directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION @@ -73,48 +72,90 @@ enum link__Purpose { EXECUTION } -type Mutation @join__type(graph: PRODUCTS) @join__type(graph: REVIEWS) { - createProduct(upc: ID!, name: String): Product @join__field(graph: PRODUCTS) - createReview(upc: ID!, id: ID!, body: String): Review - @join__field(graph: REVIEWS) +type Query + @join__type(graph: ACCOUNTS) { + a: A @join__field(graph: ACCOUNTS) + d: D @join__field(graph: ACCOUNTS) + e: E @join__field(graph: ACCOUNTS) } -type Product - @join__type(graph: INVENTORY, key: "upc") - @join__type(graph: PRODUCTS, key: "upc") - @join__type(graph: REVIEWS, key: "upc") { - - name: String @join__field(graph: PRODUCTS) - reviews: [Review] @join__field(graph: REVIEWS) - reviewsForAuthor(authorID: ID!): [Review] @join__field(graph: REVIEWS) - upc: String! +type A @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: PRODUCTS, key: "id") + @join__type(graph: REVIEWS, key: "id") { + id: ID! + a: String @join__field(graph: ACCOUNTS) @join__field(graph: REVIEWS, external: true) + b: B @join__field(graph: REVIEWS, requires: "a") @join__field(graph: PRODUCTS, external: true) + c: C @join__field(graph: PRODUCTS, requires: "b { b }") } -type Query +type B @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: PRODUCTS, key: "id") + @join__type(graph: REVIEWS, key: "id") { + id: ID! + b: String @join__field(graph: REVIEWS) @join__field(graph: PRODUCTS, external: true) +} + +type C @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: PRODUCTS, key: "id") + @join__type(graph: REVIEWS, key: "id") { + id: ID! + c: String @join__field(graph: PRODUCTS) +} + +type D + @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: PRODUCTS, key: "id") { + id: ID! + i: I @join__field(graph: ACCOUNTS) @join__field(graph: PRODUCTS, external: true) + j: String @join__field(graph: PRODUCTS, requires: "i { x ... on XY { y } ... on XZ { z } }") +} + +interface I @join__type(graph: ACCOUNTS) @join__type(graph: INVENTORY) @join__type(graph: PRODUCTS) @join__type(graph: REVIEWS) { - me: User @join__field(graph: ACCOUNTS) - topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCTS) + x: String } -type Review @join__type(graph: REVIEWS, key: "id") { - id: ID! - author: User @join__field(graph: REVIEWS, provides: "username", requires: "product {upc}") - body: String @join__field(graph: REVIEWS) - product: Product @join__field(graph: REVIEWS, requires: "author { ... on User { id username } }") +type XY implements I + @join__type(graph: INVENTORY, key: "upc") + @join__type(graph: PRODUCTS, key: "upc"){ + upc: String! + x: String + y: String @join__field(graph: PRODUCTS, external: true) +} + +type XZ implements I + @join__type(graph: INVENTORY, key: "upc") + @join__type(graph: PRODUCTS, key: "upc") { + upc: String! + x: String + z: String @join__field(graph: PRODUCTS, external: true) } -type User +type E @join__type(graph: ACCOUNTS, key: "id") - @join__type(graph: REVIEWS, key: "id") { - id: ID! - name: String - @join__field(graph: ACCOUNTS) - @join__field(graph: REVIEWS, external: true) - username: String - @join__field(graph: ACCOUNTS) - @join__field(graph: REVIEWS, external: true) - reviews: [Review] @join__field(graph: REVIEWS, , requires: "name username") + @join__type(graph: PRODUCTS, key: "id") { + id: ID! + u: U @join__field(graph: ACCOUNTS) @join__field(graph: PRODUCTS, external: true) + v: String @join__field(graph: PRODUCTS , requires: "u { ... on UA { a } ... on UB { b } }") +} + + +union U +@join__type(graph: ACCOUNTS) +@join__type(graph: PRODUCTS) +@join__type(graph: REVIEWS) += UA | UB + +type UA + @join__type(graph: PRODUCTS) { + a: String } + +type UB + @join__type(graph: REVIEWS) + @join__type(graph: PRODUCTS) { + b: String @join__field(graph: PRODUCTS, external: true) +} \ No newline at end of file From 04441ab1665d971dfa03fc3f0b997ed8cc772358 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 20 Sep 2024 17:58:27 +0200 Subject: [PATCH 5/8] failing case for requires --- .../tests/samples/basic/requires/plan.json | 102 ++++++++++++++++++ .../samples/basic/requires/supergraph.graphql | 15 ++- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/apollo-router/tests/samples/basic/requires/plan.json b/apollo-router/tests/samples/basic/requires/plan.json index 536466f9a5..1cc62d2c74 100644 --- a/apollo-router/tests/samples/basic/requires/plan.json +++ b/apollo-router/tests/samples/basic/requires/plan.json @@ -25,6 +25,24 @@ } } } + }, + { + "request": { + "body": { + "query": "query TestD__accounts__0{d{__typename id}}", + "operationName": "TestD__accounts__0" + } + }, + "response": { + "body": { + "data": { + "d": { + "__typename": "D", + "id": "1" + } + } + } + } } ] }, @@ -59,6 +77,40 @@ } } } + }, + { + "request": { + "body": { + "query": "query TestD__reviews__1($representations:[_Any!]!){_entities(representations:$representations){...on D{i{__typename x}}}}", + "operationName": "TestD__reviews__1", + "variables": { + "representations": [ + { + "__typename": "D", + "id": "1" + } + ] + } + } + }, + "response": { + "body": { + "data": { + "_entities": [ + { + "i": [ + { + "x": "xy" + }, + { + "x": "xz" + } + ] + } + ] + } + } + } } ] }, @@ -95,6 +147,43 @@ } } } + }, + { + "request": { + "body": { + "query": "query TestD__products__2($representations:[_Any!]!){_entities(representations:$representations){...on D{j}}}", + "operationName": "TestD__products__2", + "variables": { + "representations": [ + { + "__typename": "D", + "i": [ + { + "x": "xy", + "y": "y" + }, + { + "x": "xz", + "z": "z" + } + ], + "id": "1" + } + ] + } + } + }, + "response": { + "body": { + "data": { + "_entities": [ + { + "j": "j" + } + ] + } + } + } } ] } @@ -115,6 +204,19 @@ } } }, + { + "type": "Request", + "request": { + "query": "query TestD { d { j } }" + }, + "expected_response": { + "data": { + "d": { + "j": "j" + } + } + } + }, { "type": "Stop" } diff --git a/apollo-router/tests/samples/basic/requires/supergraph.graphql b/apollo-router/tests/samples/basic/requires/supergraph.graphql index 6b88b71040..3cf57070a1 100644 --- a/apollo-router/tests/samples/basic/requires/supergraph.graphql +++ b/apollo-router/tests/samples/basic/requires/supergraph.graphql @@ -104,9 +104,10 @@ type C @join__type(graph: ACCOUNTS, key: "id") type D @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: REVIEWS, key: "id") @join__type(graph: PRODUCTS, key: "id") { id: ID! - i: I @join__field(graph: ACCOUNTS) @join__field(graph: PRODUCTS, external: true) + i: [I] @join__field(graph: REVIEWS) @join__field(graph: PRODUCTS, external: true) j: String @join__field(graph: PRODUCTS, requires: "i { x ... on XY { y } ... on XZ { z } }") } @@ -119,19 +120,23 @@ interface I } type XY implements I + @join__type(graph: ACCOUNTS, key: "upc") @join__type(graph: INVENTORY, key: "upc") - @join__type(graph: PRODUCTS, key: "upc"){ + @join__type(graph: PRODUCTS) + @join__type(graph: REVIEWS, key: "upc") { upc: String! x: String - y: String @join__field(graph: PRODUCTS, external: true) + y: String @join__field(graph: REVIEWS) @join__field(graph: PRODUCTS, external: true) } type XZ implements I + @join__type(graph: ACCOUNTS, key: "upc") @join__type(graph: INVENTORY, key: "upc") - @join__type(graph: PRODUCTS, key: "upc") { + @join__type(graph: PRODUCTS, key: "upc") + @join__type(graph: REVIEWS, key: "upc") { upc: String! x: String - z: String @join__field(graph: PRODUCTS, external: true) + z: String @join__field(graph: REVIEWS) @join__field(graph: PRODUCTS, external: true) } type E From 3f20a9a62415b819da24571704d5c816bf8f5ae7 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 26 Sep 2024 11:06:34 +0200 Subject: [PATCH 6/8] pass variables test --- apollo-router/tests/samples/basic/variables/plan.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apollo-router/tests/samples/basic/variables/plan.json b/apollo-router/tests/samples/basic/variables/plan.json index d433345ec8..aab1388cf8 100644 --- a/apollo-router/tests/samples/basic/variables/plan.json +++ b/apollo-router/tests/samples/basic/variables/plan.json @@ -26,7 +26,8 @@ { "request": { "body": { - "query": "query Variables($a: String, $b: String, $c: String, $d: String) { d(arg: [\"a\", $a, \"b\"]) e(arg: { a: $b}) ...F } fragment F on Query { aa: d(arg: [\"a\", $c, \"b\"]) ab: e(arg: { a: $d})) }" + "query": "query Variables__products__0($a:String$c:String$b:String$d:String){aa:d(arg:[\"a\"$c \"b\"])ab:e(arg:{a:$d})d(arg:[\"a\"$a \"b\"])e(arg:{a:$b})}", + "operationName": "Variables__products__0" } }, "response": { From c97d3a215278f219c7752a2c61b5819e9433dd8e Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 26 Sep 2024 11:10:48 +0200 Subject: [PATCH 7/8] remove expicitely invalid query --- .../tests/samples/basic/inaccessible/plan.json | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/apollo-router/tests/samples/basic/inaccessible/plan.json b/apollo-router/tests/samples/basic/inaccessible/plan.json index a6ebc82cd5..a9f7fa044a 100644 --- a/apollo-router/tests/samples/basic/inaccessible/plan.json +++ b/apollo-router/tests/samples/basic/inaccessible/plan.json @@ -119,24 +119,6 @@ } } }, - { - "type": "Request", - "request": { - "query": "query Invalid { me { name { reviews { body } } } }" - }, - "expected_response": { - "data": { - "me": { - "name": "test", - "reviews": [ - { - "body": "test" - } - ] - } - } - } - }, { "type": "Stop" } From 0ab06cd7959ceb140f0ce3de359549b2202a75e8 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 27 Sep 2024 11:33:21 +0200 Subject: [PATCH 8/8] reorder tests --- .../tests/samples/basic/requires/plan.json | 132 ++++++++++-------- 1 file changed, 76 insertions(+), 56 deletions(-) diff --git a/apollo-router/tests/samples/basic/requires/plan.json b/apollo-router/tests/samples/basic/requires/plan.json index 1cc62d2c74..106a253c01 100644 --- a/apollo-router/tests/samples/basic/requires/plan.json +++ b/apollo-router/tests/samples/basic/requires/plan.json @@ -25,24 +25,6 @@ } } } - }, - { - "request": { - "body": { - "query": "query TestD__accounts__0{d{__typename id}}", - "operationName": "TestD__accounts__0" - } - }, - "response": { - "body": { - "data": { - "d": { - "__typename": "D", - "id": "1" - } - } - } - } } ] }, @@ -77,16 +59,23 @@ } } } - }, + } + ] + }, + "products": { + "requests": [ { "request": { "body": { - "query": "query TestD__reviews__1($representations:[_Any!]!){_entities(representations:$representations){...on D{i{__typename x}}}}", - "operationName": "TestD__reviews__1", + "query": "query TestA__products__2($representations:[_Any!]!){_entities(representations:$representations){...on A{c{c}}}}", + "operationName": "TestA__products__2", "variables": { "representations": [ { - "__typename": "D", + "__typename": "A", + "b": { + "b": "b" + }, "id": "1" } ] @@ -98,14 +87,9 @@ "data": { "_entities": [ { - "i": [ - { - "x": "xy" - }, - { - "x": "xz" - } - ] + "c": { + "c": "c" + } } ] } @@ -113,21 +97,60 @@ } } ] + } + } + }, + { + "type": "Request", + "request": { + "query": "query TestA { a { c { c } } }" + }, + "expected_response": { + "data": { + "a": { + "c": { + "c": "c" + } + } + } + } + }, + { + "type": "ReloadSubgraphs", + "subgraphs": { + "accounts": { + "requests": [ + { + "request": { + "body": { + "query": "query TestD__accounts__0{d{__typename id}}", + "operationName": "TestD__accounts__0" + } + }, + "response": { + "body": { + "data": { + "d": { + "__typename": "D", + "id": "1" + } + } + } + } + } + ] }, - "products": { + "reviews": { "requests": [ { "request": { "body": { - "query": "query TestA__products__2($representations:[_Any!]!){_entities(representations:$representations){...on A{c{c}}}}", - "operationName": "TestA__products__2", + "query": "query TestD__reviews__1($representations:[_Any!]!){_entities(representations:$representations){...on D{i{__typename x}}}}", + "operationName": "TestD__reviews__1", "variables": { "representations": [ { - "__typename": "A", - "b": { - "b": "b" - }, + "__typename": "D", "id": "1" } ] @@ -139,15 +162,24 @@ "data": { "_entities": [ { - "c": { - "c": "c" - } + "i": [ + { + "x": "xy" + }, + { + "x": "xz" + } + ] } ] } } } - }, + } + ] + }, + "products": { + "requests": [ { "request": { "body": { @@ -189,26 +221,14 @@ } } }, - { - "type": "Request", - "request": { - "query": "query TestA { a { c { c } } }" - }, - "expected_response": { - "data": { - "a": { - "c": { - "c": "c" - } - } - } - } - }, { "type": "Request", "request": { "query": "query TestD { d { j } }" }, + "headers": { + "Apollo-Expose-Query-Plan": "true" + }, "expected_response": { "data": { "d": {