5
5
6
6
import com .apollographql .federation .graphqljava .exceptions .MissingKeyException ;
7
7
import graphql .GraphQLError ;
8
+ import graphql .language .BooleanValue ;
8
9
import graphql .language .StringValue ;
9
10
import graphql .schema .Coercing ;
10
11
import graphql .schema .DataFetcher ;
11
12
import graphql .schema .DataFetcherFactory ;
12
13
import graphql .schema .FieldCoordinates ;
14
+ import graphql .schema .GraphQLAppliedDirectiveArgument ;
15
+ import graphql .schema .GraphQLArgument ;
13
16
import graphql .schema .GraphQLCodeRegistry ;
14
17
import graphql .schema .GraphQLDirectiveContainer ;
15
18
import graphql .schema .GraphQLInterfaceType ;
@@ -73,9 +76,7 @@ public SchemaTransformer setFederation2(boolean isFederation2) {
73
76
}
74
77
75
78
@ NotNull
76
- public final GraphQLSchema build () throws SchemaProblem {
77
- final List <GraphQLError > errors = new ArrayList <>();
78
-
79
+ public GraphQLSchema build () throws SchemaProblem {
79
80
// Make new Schema
80
81
final GraphQLSchema .Builder newSchema = GraphQLSchema .newSchema (originalSchema );
81
82
@@ -98,31 +99,7 @@ public final GraphQLSchema build() throws SchemaProblem {
98
99
newSchema .query (newQueryType .build ());
99
100
100
101
final GraphQLCodeRegistry .Builder newCodeRegistry =
101
- GraphQLCodeRegistry .newCodeRegistry (originalSchema .getCodeRegistry ());
102
-
103
- if (!entityTypeNames .isEmpty ()) {
104
- if (entityTypeResolver != null ) {
105
- newCodeRegistry .typeResolver (_Entity .typeName , entityTypeResolver );
106
- } else {
107
- if (!newCodeRegistry .hasTypeResolver (_Entity .typeName )) {
108
- errors .add (new FederationError ("Missing a type resolver for _Entity" ));
109
- }
110
- }
111
-
112
- final FieldCoordinates _entities =
113
- FieldCoordinates .coordinates (originalQueryType .getName (), _Entity .fieldName );
114
- if (entitiesDataFetcher != null ) {
115
- newCodeRegistry .dataFetcher (_entities , entitiesDataFetcher );
116
- } else if (entitiesDataFetcherFactory != null ) {
117
- newCodeRegistry .dataFetcher (_entities , entitiesDataFetcherFactory );
118
- } else if (!newCodeRegistry .hasDataFetcher (_entities )) {
119
- errors .add (new FederationError ("Missing a data fetcher for _entities" ));
120
- }
121
- }
122
-
123
- if (!errors .isEmpty ()) {
124
- throw new SchemaProblem (errors );
125
- }
102
+ updateCodeRegistryWithEntityResolvers (entityTypeNames );
126
103
127
104
// expose the schema as _service.sdl
128
105
newCodeRegistry .dataFetcher (
@@ -168,7 +145,7 @@ Set<String> getFederatedEntities() {
168
145
.map (type -> (GraphQLObjectType ) type )
169
146
.forEach (
170
147
type ->
171
- type .getInterfaces (). stream ()
148
+ type .getInterfaces ()
172
149
.forEach (
173
150
intf -> {
174
151
if (interfaceEntities .contains (intf )) {
@@ -224,6 +201,89 @@ private Set<String> retrieveFieldSets(GraphQLDirectiveContainer type) {
224
201
return fieldSets ;
225
202
}
226
203
204
+ private GraphQLCodeRegistry .Builder updateCodeRegistryWithEntityResolvers (
205
+ Set <String > entityTypeNames ) {
206
+ final List <GraphQLError > errors = new ArrayList <>();
207
+ final GraphQLCodeRegistry .Builder newCodeRegistry =
208
+ GraphQLCodeRegistry .newCodeRegistry (originalSchema .getCodeRegistry ());
209
+
210
+ if (!entityTypeNames .isEmpty ()) {
211
+ final boolean areEntitiesResolvable = resolvableEntitiesExist (entityTypeNames );
212
+ if (entityTypeResolver != null ) {
213
+ newCodeRegistry .typeResolver (_Entity .typeName , entityTypeResolver );
214
+ } else if (!areEntitiesResolvable ) {
215
+ // add fake entity union resolver as this type will never be resolved locally
216
+ newCodeRegistry .typeResolver (
217
+ _Entity .typeName ,
218
+ env -> {
219
+ throw new IllegalStateException (
220
+ "_Entity type resolver should never be called on non-resolvable entities" );
221
+ });
222
+ } else if (!newCodeRegistry .hasTypeResolver (_Entity .typeName )) {
223
+ errors .add (new FederationError ("Missing a type resolver for _Entity" ));
224
+ }
225
+
226
+ if (areEntitiesResolvable ) {
227
+ // need _entities resolver
228
+ final FieldCoordinates _entities =
229
+ FieldCoordinates .coordinates (
230
+ originalSchema .getQueryType ().getName (), _Entity .fieldName );
231
+ if (entitiesDataFetcher != null ) {
232
+ newCodeRegistry .dataFetcher (_entities , entitiesDataFetcher );
233
+ } else if (entitiesDataFetcherFactory != null ) {
234
+ newCodeRegistry .dataFetcher (_entities , entitiesDataFetcherFactory );
235
+ } else if (!newCodeRegistry .hasDataFetcher (_entities )) {
236
+ errors .add (new FederationError ("Missing a data fetcher for _entities" ));
237
+ }
238
+ }
239
+ }
240
+
241
+ if (!errors .isEmpty ()) {
242
+ throw new SchemaProblem (errors );
243
+ }
244
+
245
+ return newCodeRegistry ;
246
+ }
247
+
248
+ private boolean resolvableEntitiesExist (Set <String > entityNames ) {
249
+ return entityNames .stream ()
250
+ .anyMatch (
251
+ entity -> {
252
+ GraphQLObjectType entityObject = (GraphQLObjectType ) originalSchema .getType (entity );
253
+ boolean isResolvable =
254
+ entityObject .getAppliedDirectives (FederationDirectives .keyName ).stream ()
255
+ .anyMatch (
256
+ key -> {
257
+ GraphQLAppliedDirectiveArgument resolvable =
258
+ key .getArgument ("resolvable" );
259
+ if (resolvable != null ) {
260
+ BooleanValue resolvableValue =
261
+ (BooleanValue ) resolvable .getArgumentValue ().getValue ();
262
+ return resolvableValue == null || resolvableValue .isValue ();
263
+ } else {
264
+ return true ;
265
+ }
266
+ });
267
+
268
+ if (!isResolvable ) {
269
+ // fallback to also verify old directive definitions
270
+ return entityObject .getDirectives (FederationDirectives .keyName ).stream ()
271
+ .anyMatch (
272
+ key -> {
273
+ GraphQLArgument resolvable = key .getArgument ("resolvable" );
274
+ if (resolvable != null ) {
275
+ BooleanValue resolvableValue =
276
+ (BooleanValue ) resolvable .getArgumentValue ().getValue ();
277
+ return resolvableValue == null || resolvableValue .isValue ();
278
+ }
279
+ return true ;
280
+ });
281
+ } else {
282
+ return true ;
283
+ }
284
+ });
285
+ }
286
+
227
287
/**
228
288
* Generate Apollo Federation v1 compatible SDL that should be returned from `_service { sdl }`
229
289
* query.
0 commit comments