Skip to content

Commit d730ff5

Browse files
authored
ensure scalars are wired up if no existing wiring (#382)
Fixes #380 This restructures the scalar transformation logic to ensure that both the type definition and wiring are added correctly if necessary. This allows consumers to define the scalars in their schema, but not assign runtime wiring. This can be nice to support other tools like linters or graphql-faker that require the schema to be a valid schema file.
1 parent 149e4c5 commit d730ff5

File tree

4 files changed

+205
-9
lines changed

4 files changed

+205
-9
lines changed

graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/Federation.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,18 @@ private static RuntimeWiring ensureFederationV2DirectiveDefinitionsExist(
163163
if (def instanceof DirectiveDefinition
164164
&& !typeRegistry.getDirectiveDefinition(def.getName()).isPresent()) {
165165
typeRegistry.add(def);
166-
} else if (def instanceof ScalarTypeDefinition
167-
&& !typeRegistry.scalars().containsKey(def.getName())) {
168-
typeRegistry.add(def);
169-
scalarTypesToAdd.add(
170-
GraphQLScalarType.newScalar()
171-
.name(def.getName())
172-
.description(null)
173-
.coercing(_Any.type.getCoercing())
174-
.build());
166+
} else if (def instanceof ScalarTypeDefinition) {
167+
if (!typeRegistry.scalars().containsKey(def.getName())) {
168+
typeRegistry.add(def);
169+
}
170+
if (!runtimeWiring.getScalars().containsKey(def.getName())) {
171+
scalarTypesToAdd.add(
172+
GraphQLScalarType.newScalar()
173+
.name(def.getName())
174+
.description(null)
175+
.coercing(_Any.type.getCoercing())
176+
.build());
177+
}
175178
} else if (def instanceof EnumTypeDefinition
176179
&& !typeRegistry.types().containsKey(def.getName())) {
177180
typeRegistry.add(def);

graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/FederationTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,11 @@ public void verifyFederationV2Transformation_progressiveOverride() {
356356
() -> Federation.transform(schemaSDL).fetchEntities(env -> null).build());
357357
}
358358

359+
@Test
360+
public void verifyFederationV2Transformation_scalarsDefinedInSchemaButNotWired() {
361+
verifyFederationTransformation("schemas/federationV2_defined_scalars.graphql", true);
362+
}
363+
359364
private GraphQLSchema verifyFederationTransformation(
360365
String schemaFileName, boolean isFederationV2) {
361366
final RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().build();
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
extend schema
2+
@link(
3+
url: "https://specs.apollo.dev/federation/v2.3"
4+
import: [
5+
"@composeDirective"
6+
"@extends"
7+
"@external"
8+
"@key"
9+
"@inaccessible"
10+
"@interfaceObject"
11+
"@override"
12+
"@provides"
13+
"@requires"
14+
"@shareable"
15+
"@tag"
16+
]
17+
)
18+
@link(url: "https://myspecs.dev/myCustomDirective/v1.0", import: ["@custom"])
19+
@composeDirective(name: "@custom")
20+
21+
scalar federation__FieldSet
22+
23+
directive @custom on OBJECT
24+
directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
25+
26+
type Product
27+
@custom
28+
@key(fields: "id")
29+
@key(fields: "sku package")
30+
@key(fields: "sku variation { id }") {
31+
id: ID!
32+
sku: String
33+
package: String
34+
variation: ProductVariation
35+
dimensions: ProductDimension
36+
createdBy: User @provides(fields: "totalProductsCreated")
37+
notes: String @tag(name: "internal")
38+
research: [ProductResearch!]!
39+
}
40+
41+
type DeprecatedProduct @key(fields: "sku package") {
42+
sku: String!
43+
package: String!
44+
reason: String
45+
createdBy: User
46+
}
47+
48+
type ProductVariation {
49+
id: ID!
50+
}
51+
52+
type ProductResearch @key(fields: "study { caseNumber }") {
53+
study: CaseStudy!
54+
outcome: String
55+
}
56+
57+
type CaseStudy {
58+
caseNumber: ID!
59+
description: String
60+
}
61+
62+
type ProductDimension @shareable {
63+
size: String
64+
weight: Float
65+
unit: String @inaccessible
66+
}
67+
68+
type Query {
69+
product(id: ID!): Product
70+
deprecatedProduct(sku: String!, package: String!): DeprecatedProduct @deprecated(reason: "Use product query instead")
71+
}
72+
73+
type User @key(fields: "email") {
74+
averageProductsCreatedPerYear: Int @requires(fields: "totalProductsCreated yearsOfEmployment")
75+
email: ID! @external
76+
name: String @override(from: "users")
77+
totalProductsCreated: Int @external
78+
yearsOfEmployment: Int! @external
79+
}
80+
81+
type Inventory @interfaceObject @key(fields: "id") {
82+
id: ID!
83+
deprecatedProducts: [DeprecatedProduct!]!
84+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
schema @composeDirective(name : "@custom") @link(import : ["@composeDirective", "@extends", "@external", "@key", "@inaccessible", "@interfaceObject", "@override", "@provides", "@requires", "@shareable", "@tag"], url : "https://specs.apollo.dev/federation/v2.3") @link(import : ["@custom"], url : "https://myspecs.dev/myCustomDirective/v1.0"){
2+
query: Query
3+
}
4+
5+
directive @composeDirective(name: String!) repeatable on SCHEMA
6+
7+
directive @custom on OBJECT
8+
9+
directive @extends on OBJECT | INTERFACE
10+
11+
directive @external on OBJECT | FIELD_DEFINITION
12+
13+
directive @inaccessible on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
14+
15+
directive @interfaceObject on OBJECT
16+
17+
directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
18+
19+
directive @link(as: String, for: link__Purpose, import: [link__Import], url: String!) repeatable on SCHEMA
20+
21+
directive @override(from: String!) on FIELD_DEFINITION
22+
23+
directive @provides(fields: federation__FieldSet!) on FIELD_DEFINITION
24+
25+
directive @requires(fields: federation__FieldSet!) on FIELD_DEFINITION
26+
27+
directive @shareable repeatable on OBJECT | FIELD_DEFINITION
28+
29+
directive @tag(name: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
30+
31+
union _Entity = DeprecatedProduct | Inventory | Product | ProductResearch | User
32+
33+
type CaseStudy {
34+
caseNumber: ID!
35+
description: String
36+
}
37+
38+
type DeprecatedProduct @key(fields : "sku package", resolvable : true) {
39+
createdBy: User
40+
package: String!
41+
reason: String
42+
sku: String!
43+
}
44+
45+
type Inventory @interfaceObject @key(fields : "id", resolvable : true) {
46+
deprecatedProducts: [DeprecatedProduct!]!
47+
id: ID!
48+
}
49+
50+
type Product @custom @key(fields : "id", resolvable : true) @key(fields : "sku package", resolvable : true) @key(fields : "sku variation { id }", resolvable : true) {
51+
createdBy: User @provides(fields : "totalProductsCreated")
52+
dimensions: ProductDimension
53+
id: ID!
54+
notes: String @tag(name : "internal")
55+
package: String
56+
research: [ProductResearch!]!
57+
sku: String
58+
variation: ProductVariation
59+
}
60+
61+
type ProductDimension @shareable {
62+
size: String
63+
unit: String @inaccessible
64+
weight: Float
65+
}
66+
67+
type ProductResearch @key(fields : "study { caseNumber }", resolvable : true) {
68+
outcome: String
69+
study: CaseStudy!
70+
}
71+
72+
type ProductVariation {
73+
id: ID!
74+
}
75+
76+
type Query {
77+
_entities(representations: [_Any!]!): [_Entity]!
78+
_service: _Service!
79+
deprecatedProduct(package: String!, sku: String!): DeprecatedProduct @deprecated(reason : "Use product query instead")
80+
product(id: ID!): Product
81+
}
82+
83+
type User @key(fields : "email", resolvable : true) {
84+
averageProductsCreatedPerYear: Int @requires(fields : "totalProductsCreated yearsOfEmployment")
85+
email: ID! @external
86+
name: String @override(from : "users")
87+
totalProductsCreated: Int @external
88+
yearsOfEmployment: Int! @external
89+
}
90+
91+
type _Service {
92+
sdl: String!
93+
}
94+
95+
enum link__Purpose {
96+
EXECUTION
97+
SECURITY
98+
}
99+
100+
scalar _Any
101+
102+
scalar federation__FieldSet
103+
104+
scalar link__Import

0 commit comments

Comments
 (0)