@@ -302,20 +302,43 @@ public static Initializer initializer() {
302302 public interface Initializer {
303303
304304 /**
305- * Provide a function to derive the simple class name that corresponds to a
306- * GraphQL union member type, or a GraphQL interface implementation type.
307- * This is then used to find a Java class in the same package as that of
308- * the return type of the controller method for the interface or union.
309- * <p>The default, {@link GraphQLObjectType#getName()} is used
305+ * Provide an explicit mapping between a GraphQL type name and the Java
306+ * class(es) that represent it at runtime to help inspect union member
307+ * and interface implementation types when those associations cannot be
308+ * discovered otherwise.
309+ * <p>Out of the box, there a several ways through which schema inspection
310+ * can locate such types automatically:
311+ * <ul>
312+ * <li>Java class representations are located in the same package as the
313+ * type returned from the controller method for a union or interface field,
314+ * and their {@link Class#getSimpleName() simple class names} match GraphQL
315+ * type names, possibly with the help of a {@link #classNameFunction}.
316+ * <li>Java class representations are located in the same package as the
317+ * declaring class of the controller method for a union or interface field.
318+ * <li>Controller methods return the Java class representations of schema
319+ * fields for concrete union member or interface implementation types.
320+ * </ul>
321+ * @param graphQlTypeName the name of a GraphQL Object type
322+ * @param aClass one or more Java class representations
323+ * @return the same initializer instance
324+ */
325+ Initializer classMapping (String graphQlTypeName , Class <?>... aClass );
326+
327+ /**
328+ * Help to derive the {@link Class#getSimpleName() simple class name} for
329+ * the Java representation of a GraphQL union member or interface implementing
330+ * type. For more details, see {@link #classMapping(String, Class[])}.
331+ * <p>By default, {@link GraphQLObjectType#getName()} is used.
310332 * @param function the function to use
311333 * @return the same initializer instance
312334 */
313335 Initializer classNameFunction (Function <GraphQLObjectType , String > function );
314336
315337 /**
316- * Add a custom {@link ClassResolver} to use to find the Java class for a
317- * GraphQL union member type, or a GraphQL interface implementation type.
318- * @param resolver the resolver to add
338+ * Alternative to {@link #classMapping(String, Class[])} with a custom
339+ * {@link ClassResolver} to find the Java class(es) for a GraphQL union
340+ * member or interface implementation type.
341+ * @param resolver the resolver to use to find associated Java classes
319342 * @return the same initializer instance
320343 */
321344 Initializer classResolver (ClassResolver resolver );
@@ -345,14 +368,6 @@ public interface ClassResolver {
345368 */
346369 List <Class <?>> resolveClass (GraphQLObjectType objectType , GraphQLNamedOutputType interfaceOrUnionType );
347370
348-
349- /**
350- * Create a resolver from the given mappings.
351- * @param mappings from Class to GraphQL type name
352- */
353- static ClassResolver create (Map <Class <?>, String > mappings ) {
354- return new MappingClassResolver (mappings );
355- }
356371 }
357372
358373
@@ -365,6 +380,8 @@ private static final class DefaultInitializer implements Initializer {
365380
366381 private final List <ClassResolver > classResolvers = new ArrayList <>();
367382
383+ private final MultiValueMap <String , Class <?>> classMappings = new LinkedMultiValueMap <>();
384+
368385 @ Override
369386 public Initializer classNameFunction (Function <GraphQLObjectType , String > function ) {
370387 this .classNameFunction = function ;
@@ -378,13 +395,19 @@ public Initializer classResolver(ClassResolver resolver) {
378395 }
379396
380397 @ Override
381- public SchemaReport inspect (GraphQLSchema schema , Map <String , Map <String , DataFetcher >> fetchers ) {
398+ public Initializer classMapping (String graphQlTypeName , Class <?>... classes ) {
399+ for (Class <?> aClass : classes ) {
400+ this .classMappings .add (graphQlTypeName , aClass );
401+ }
402+ return this ;
403+ }
382404
383- ReflectionClassResolver reflectionResolver =
384- ReflectionClassResolver . create ( schema , fetchers , this . classNameFunction );
405+ @ Override
406+ public SchemaReport inspect ( GraphQLSchema schema , Map < String , Map < String , DataFetcher >> fetchers ) {
385407
386408 List <ClassResolver > resolvers = new ArrayList <>(this .classResolvers );
387- resolvers .add (reflectionResolver );
409+ resolvers .add (new MappingClassResolver (this .classMappings ));
410+ resolvers .add (ReflectionClassResolver .create (schema , fetchers , this .classNameFunction ));
388411
389412 InterfaceUnionLookup lookup = InterfaceUnionLookup .create (schema , resolvers );
390413
@@ -399,15 +422,15 @@ public SchemaReport inspect(GraphQLSchema schema, Map<String, Map<String, DataFe
399422 */
400423 private static final class MappingClassResolver implements ClassResolver {
401424
402- private final MultiValueMap <String , Class <?>> map = new LinkedMultiValueMap <>();
425+ private final MultiValueMap <String , Class <?>> mappings = new LinkedMultiValueMap <>();
403426
404- MappingClassResolver (Map < Class <?>, String > mappings ) {
405- mappings . forEach (( key , value ) -> this .map . add ( value , key ) );
427+ MappingClassResolver (MultiValueMap < String , Class <?>> mappings ) {
428+ this .mappings . putAll ( mappings );
406429 }
407430
408431 @ Override
409432 public List <Class <?>> resolveClass (GraphQLObjectType objectType , GraphQLNamedOutputType interfaceOrUnionType ) {
410- return this .map .getOrDefault (objectType .getName (), Collections .emptyList ());
433+ return this .mappings .getOrDefault (objectType .getName (), Collections .emptyList ());
411434 }
412435 }
413436
@@ -478,7 +501,7 @@ public static ReflectionClassResolver create(
478501 if (PACKAGE_PREDICATE .test (clazz .getPackageName ())) {
479502 addClassPrefix (outputTypeName , clazz , classPrefixes );
480503 }
481- else if (dataFetcher instanceof SelfDescribingDataFetcher <?> selfDescribing ) {
504+ if (dataFetcher instanceof SelfDescribingDataFetcher <?> selfDescribing ) {
482505 if (selfDescribing .getReturnType ().getSource () instanceof MethodParameter param ) {
483506 addClassPrefix (outputTypeName , param .getDeclaringClass (), classPrefixes );
484507 }
0 commit comments