graph-quilt / graph-quilt/graphql-orchestrator-java

Schema Validation problems in Schema Generated by getExecutableSchema

Open
#198 3 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Groovy
Stars
73
Forks
32
PR merge metrics
No merged PRs in 30d

Description

**Describe the bug**
When I use orchestrator and merge two subgraphs with Apollo Federation Directives as given below ,

```
String inventorySchema = "type Product @key(fields: \"upc\") @extends {\n" +
" upc: String! @external\n" +
" weight: Int @external\n" +
" price: Int @external\n" +
" inStock: Boolean\n" +
" shippingEstimate: Int @requires(fields: \"price weight\")\n" +
"}";
String productSchema = "type Query {\n" +
" topProducts(first: Int = 5): [Product]\n" +
" productB (upc : String!): Product\n" +
"}\n" +
"\n" +
"type Product @key(fields: \"upc\") {\n" +
" upc: String!\n" +
" name: String\n" +
" price: Int\n" +
" weight: Int\n" +
"}\n";
String reviewSchema = "type Review @key(fields: \"id\") {\n" +
" id: ID!\n" +
" body: String\n" +
" product: Product @resolver(field:\"productB\", arguments: [{name : \"upc\", value: \"UPC001\"}])\n" +
"}\n" +
"\n" +
"type User @key(fields: \"id\") @extends {\n" +
" id: ID! @external\n" +
" username: String @external\n" +
" reviews: [Review]\n" +
"}\n" +
"\n" +
"type Product @key(fields: \"upc\") @extends {\n" +
" upc: String! @external\n" +
" reviews: [Review]\n" +
"}\n" +
"\n" +
"# ================================\n" +
"# define this as built-in directive\n" +
"directive @resolver(field: String!, arguments: [ResolverArgument!]) on FIELD_DEFINITION\n" +
"\n" +
"# define this as built-in type\n" +
"input ResolverArgument {\n" +
" name : String!\n" +
" value : String!\n" +
"}";
String userSchema = "type Query {\n" +
" me: User\n" +
"}\n" +
"\n" +
"type User @key(fields: \"id\") {\n" +
" id: ID!\n" +
" name: String\n" +
" username: String\n" +
"}";

// Creating providers for your GraphQL services
GenericProvider inventoryService = new GenericProvider("http://localhost:8084/graphql", httpClient,
inventorySchema, "inventory");
GenericProvider productService = new GenericProvider("http://localhost:8081/graphql", httpClient,
productSchema, "product");
GenericProvider reviewService = new GenericProvider("http://localhost:8083/graphql", httpClient,
reviewSchema, "review");
GenericProvider accountService = new GenericProvider("http://localhost:8082/graphql", httpClient,
userSchema, "user");

RuntimeGraph runtimeGraph = SchemaStitcher.newBuilder()
.service(accountService)
.service(productService)
.service(inventoryService)
.service(reviewService)
.build()
.stitchGraph();
```

The stitched Schema when output through
```
String printSchema = new SchemaPrinter().print(runtimeGraph.getExecutableSchema());
System.out.println(printSchema);
```

produces the output

```
"Directs the executor to include this field or fragment only when the `if` argument is true"
directive @include(
"Included when true."
if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

"Directs the executor to skip this field or fragment when the `if`'argument is true."
directive @skip(
"Skipped when true."
if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @provides(fields: String!) on FIELD_DEFINITION

"Exposes a URL that specifies the behaviour of this scalar."
directive @specifiedBy(
"The URL that specifies the behaviour of this scalar."
url: String!
) on SCALAR

directive @extends on OBJECT | INTERFACE

directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE

directive @key(fields: _FieldSet) repeatable on OBJECT | INTERFACE

directive @requires(fields: _FieldSet!) on FIELD_DEFINITION

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @tag(name: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

directive @external on FIELD_DEFINITION

"[product]"
type Product {
inStock: Boolean
name: String
price: Int
reviews: [Review]
shippingEstimate: Int
upc: String!
weight: Int
}

"[]"
type Query {
_namespace: String
me: User
productB(upc: String!): Product
topProducts(first: Int = 5): [Product]
}

"[review]"
type Review @key(fields : "id") {
body: String
id: ID!
product: Product @resolver(arguments : [{name : "upc", value : "UPC001"}], field : "productB")
}

"[user]"
type User {
id: ID!
name: String
reviews: [Review]
username: String
}

"A selection set"
scalar _FieldSet

"[review]"
input ResolverArgument {
name: String!
value: String!
}
```

When this is parsed
```
SchemaParser parser = new SchemaParser();
TypeDefinitionRegistry typeDefinitionRegistry = parser.parse(Paths.get("src/main/resources/supergraph.graphqls").toFile());
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().build();
GraphQLSchema federatedSchema = Federation.transform(typeDefinitionRegistry, runtimeWiring)
.build();
System.out.println(federatedSchema);
```
There are multiple issues that arise as below ,

`SchemaProblem{errors=['product' [@50:3] tried to use an undeclared directive 'resolver']}`

`SchemaProblem{errors=['include' type [@28:1] tried to redefine existing directive 'include' type [@1:1], 'skip' type [@30:1] tried to redefine existing directive 'skip' type [@6:1]]}`

When I remove the duplicate entries for @include and @skip and add the
`directive @resolver(field: String!, arguments: [ResolverArgument!]) on FIELD_DEFINITION`

the validation is successful

Can you please let me know how this can be fixed.

I further want to know if this stitched graph can be considered and treated as a supergraph similar to the one in APOLLO Federation.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.