14
14
import graphql .language .TypeExtensionDefinition ;
15
15
import graphql .language .TypeName ;
16
16
import graphql .language .UnionTypeDefinition ;
17
+ import graphql .schema .idl .errors .InterfaceFieldArgumentRedefinitionError ;
18
+ import graphql .schema .idl .errors .InterfaceFieldRedefinitionError ;
19
+ import graphql .schema .idl .errors .MissingInterfaceFieldArgumentsError ;
20
+ import graphql .schema .idl .errors .MissingInterfaceFieldError ;
17
21
import graphql .schema .idl .errors .MissingInterfaceTypeError ;
18
22
import graphql .schema .idl .errors .MissingScalarImplementationError ;
19
23
import graphql .schema .idl .errors .MissingTypeError ;
@@ -48,6 +52,8 @@ public List<GraphQLError> checkTypeRegistry(TypeDefinitionRegistry typeRegistry,
48
52
checkTypeExtensionsHaveCorrespondingType (errors , typeRegistry );
49
53
checkTypeExtensionsFieldRedefinition (errors , typeRegistry );
50
54
55
+ checkInterfacesAreImplemented (errors , typeRegistry );
56
+
51
57
checkSchemaInvariants (errors , typeRegistry );
52
58
53
59
checkScalarImplementationsArePresent (errors , typeRegistry , wiring );
@@ -136,7 +142,6 @@ private void checkForMissingTypes(List<GraphQLError> errors, TypeDefinitionRegis
136
142
});
137
143
}
138
144
139
-
140
145
private void checkScalarImplementationsArePresent (List <GraphQLError > errors , TypeDefinitionRegistry typeRegistry , RuntimeWiring wiring ) {
141
146
typeRegistry .scalars ().keySet ().forEach (scalarName -> {
142
147
if (!wiring .getScalars ().containsKey (scalarName )) {
@@ -145,6 +150,7 @@ private void checkScalarImplementationsArePresent(List<GraphQLError> errors, Typ
145
150
});
146
151
}
147
152
153
+
148
154
private void checkTypeResolversArePresent (List <GraphQLError > errors , TypeDefinitionRegistry typeRegistry , RuntimeWiring wiring ) {
149
155
150
156
Consumer <TypeDefinition > checkForResolver = typeDef -> {
@@ -159,7 +165,6 @@ private void checkTypeResolversArePresent(List<GraphQLError> errors, TypeDefinit
159
165
160
166
}
161
167
162
-
163
168
private void checkFieldTypesPresent (TypeDefinitionRegistry typeRegistry , List <GraphQLError > errors , TypeDefinition typeDefinition , List <FieldDefinition > fields ) {
164
169
List <Type > fieldTypes = fields .stream ().map (FieldDefinition ::getType ).collect (Collectors .toList ());
165
170
fieldTypes .forEach (checkTypeExists ("field" , typeRegistry , errors , typeDefinition ));
@@ -175,6 +180,7 @@ private void checkFieldTypesPresent(TypeDefinitionRegistry typeRegistry, List<Gr
175
180
fieldInputValues .forEach (checkTypeExists ("field input" , typeRegistry , errors , typeDefinition ));
176
181
}
177
182
183
+
178
184
private Consumer <Type > checkTypeExists (String typeOfType , TypeDefinitionRegistry typeRegistry , List <GraphQLError > errors , TypeDefinition typeDefinition ) {
179
185
return t -> {
180
186
TypeName unwrapped = TypeInfo .typeInfo (t ).getTypeName ();
@@ -197,6 +203,76 @@ private Consumer<? super Type> checkInterfaceTypeExists(TypeDefinitionRegistry t
197
203
};
198
204
}
199
205
206
+ private void checkInterfacesAreImplemented (List <GraphQLError > errors , TypeDefinitionRegistry typeRegistry ) {
207
+ Map <String , TypeDefinition > typesMap = typeRegistry .types ();
208
+
209
+ // objects
210
+ List <ObjectTypeDefinition > objectTypes = filterTo (typesMap , ObjectTypeDefinition .class );
211
+ objectTypes .forEach (objectType -> {
212
+ List <Type > implementsTypes = objectType .getImplements ();
213
+ implementsTypes .forEach (checkInterfaceIsImplemented ("object" , typeRegistry , errors , objectType ));
214
+ });
215
+
216
+ Map <String , List <TypeExtensionDefinition >> typeExtensions = typeRegistry .typeExtensions ();
217
+ typeExtensions .values ().forEach (extList -> extList .forEach (typeExtension -> {
218
+ List <Type > implementsTypes = typeExtension .getImplements ();
219
+ implementsTypes .forEach (checkInterfaceIsImplemented ("extension" , typeRegistry , errors , typeExtension ));
220
+ }));
221
+ }
222
+
223
+ private Consumer <? super Type > checkInterfaceIsImplemented (String typeOfType , TypeDefinitionRegistry typeRegistry , List <GraphQLError > errors , ObjectTypeDefinition objectTypeDef ) {
224
+ return t -> {
225
+ TypeInfo typeInfo = TypeInfo .typeInfo (t );
226
+ TypeName unwrapped = typeInfo .getTypeName ();
227
+ Optional <TypeDefinition > type = typeRegistry .getType (unwrapped );
228
+ // previous checks handle the missing case and wrong type case
229
+ if (type .isPresent () && type .get () instanceof InterfaceTypeDefinition ) {
230
+ InterfaceTypeDefinition interfaceTypeDef = (InterfaceTypeDefinition ) type .get ();
231
+
232
+ Map <String , FieldDefinition > objectFields = objectTypeDef .getFieldDefinitions ().stream ()
233
+ .collect (Collectors .toMap (
234
+ FieldDefinition ::getName , Function .identity ()
235
+ ));
236
+
237
+ interfaceTypeDef .getFieldDefinitions ().forEach (interfaceFieldDef -> {
238
+ FieldDefinition objectFieldDef = objectFields .get (interfaceFieldDef .getName ());
239
+ if (objectFieldDef == null ) {
240
+ errors .add (new MissingInterfaceFieldError (typeOfType , objectTypeDef , interfaceTypeDef , interfaceFieldDef ));
241
+ } else {
242
+ String interfaceFieldType = AstPrinter .printAst (interfaceFieldDef .getType ());
243
+ String objectFieldType = AstPrinter .printAst (objectFieldDef .getType ());
244
+ if (!interfaceFieldType .equals (objectFieldType )) {
245
+ errors .add (new InterfaceFieldRedefinitionError (typeOfType , objectTypeDef , interfaceTypeDef , objectFieldDef , objectFieldType , interfaceFieldType ));
246
+ }
247
+
248
+ // look at arguments
249
+ List <InputValueDefinition > objectArgs = objectFieldDef .getInputValueDefinitions ();
250
+ List <InputValueDefinition > interfaceArgs = interfaceFieldDef .getInputValueDefinitions ();
251
+ if (objectArgs .size () != interfaceArgs .size ()) {
252
+ errors .add (new MissingInterfaceFieldArgumentsError (typeOfType , objectTypeDef , interfaceTypeDef , objectFieldDef ));
253
+ } else {
254
+ checkArgumentConsistency (typeOfType , objectTypeDef , interfaceTypeDef , objectFieldDef , interfaceFieldDef , errors );
255
+ }
256
+ }
257
+ });
258
+ }
259
+ };
260
+ }
261
+
262
+ private void checkArgumentConsistency (String typeOfType , ObjectTypeDefinition objectTypeDef , InterfaceTypeDefinition interfaceTypeDef , FieldDefinition objectFieldDef , FieldDefinition interfaceFieldDef , List <GraphQLError > errors ) {
263
+ List <InputValueDefinition > objectArgs = objectFieldDef .getInputValueDefinitions ();
264
+ List <InputValueDefinition > interfaceArgs = interfaceFieldDef .getInputValueDefinitions ();
265
+ for (int i = 0 ; i < interfaceArgs .size (); i ++) {
266
+ InputValueDefinition interfaceArg = interfaceArgs .get (i );
267
+ InputValueDefinition objectArg = objectArgs .get (i );
268
+ String interfaceArgStr = AstPrinter .printAst (interfaceArg );
269
+ String objectArgStr = AstPrinter .printAst (objectArg );
270
+ if (!interfaceArgStr .equals (objectArgStr )) {
271
+ errors .add (new InterfaceFieldArgumentRedefinitionError (typeOfType , objectTypeDef , interfaceTypeDef , objectFieldDef , objectArgStr , interfaceArgStr ));
272
+ }
273
+ }
274
+ }
275
+
200
276
/*
201
277
A type can re-define a field if its actual the same type, but if they make 'fieldA : String' into
202
278
'fieldA : Int' then we cant handle that. Even 'fieldA : String' to 'fieldA: String!' is tough to handle
0 commit comments