Polishing contribution
Closes gh-202
This commit is contained in:
@@ -671,6 +671,10 @@ Schema mapping handler methods can have any of the following method arguments:
|
||||
| For access to field arguments with conversion.
|
||||
See <<controllers-schema-mapping-argument>>.
|
||||
|
||||
| `@ProjectedPayload` Interface
|
||||
| For access to field arguments through a project interface.
|
||||
See <<controllers-schema-mapping-projectedpayload-argument>>.
|
||||
|
||||
| Source
|
||||
| For access to the source (i.e. parent/container) instance of the field.
|
||||
See <<controllers-schema-mapping-source>>.
|
||||
@@ -740,50 +744,23 @@ You can use `@Argument` on a `Map<String, Object>` argument, to obtain all argum
|
||||
values. The name attribute on `@Argument` must not be set.
|
||||
|
||||
|
||||
[[controllers-schema-mapping-source]]
|
||||
==== Source
|
||||
[[controllers-schema-mapping-projectedpayload-argument]]
|
||||
==== `@ProjectPayload` Interface
|
||||
|
||||
In GraphQL Java, the `DataFetchingEnvironment` provides access to the source (i.e.
|
||||
parent/container) instance of the field. To access this, simply declare a method parameter
|
||||
of the expected target type.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class BookController {
|
||||
|
||||
@SchemaMapping
|
||||
public Author author(Book book) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The source method argument also helps to determine the type name for the mapping.
|
||||
If the simple name of the Java class matches the GraphQL type, then there is no need to
|
||||
explicitly specify the type name in the `@SchemaMapping` annotation.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
A <<controllers-batch-mapping>> handler method can batch load all authors for a query,
|
||||
given a list of source/parent books objects.
|
||||
====
|
||||
|
||||
|
||||
[[controllers-schema-mapping-argument-projections]]
|
||||
==== Argument Projections
|
||||
|
||||
When accessing individual arguments from a GraphQL request, interface projections can
|
||||
be useful to access arguments through a well-defined interface.
|
||||
|
||||
Spring Data's `@ProjectedPayload` can be used to annotate projection interfaces that
|
||||
can be declared as handler method arguments. Payload projection can work on top-level
|
||||
arguments (`DataFetchingEnvironment.getArguments()`). Alternatively, projections can
|
||||
be applied on individual arguments by using `@Argument` with a projected payload interface.
|
||||
|
||||
Argument projections are provided by https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces[Spring Data's Interface projections]
|
||||
As an alternative to using complete Objects with <<controllers-schema-mapping-argument>>,
|
||||
you can also use a projection interface to access GraphQL request arguments through a
|
||||
well-defined, minimal interface. Argument projections are provided by
|
||||
https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces[Spring Data's Interface projections]
|
||||
when Spring Data is on the class path.
|
||||
|
||||
To make use of this, create an interface annotated with `@ProjectedPayload` and declare
|
||||
it as a controller method parameter. If the parameter is annotated with `@Argument`,
|
||||
it applies to an individual argument within the `DataFetchingEnvironment.getArguments()`
|
||||
map. When declared without `@Argument`, the projection works on top-level arguments in
|
||||
the complete arguments map.
|
||||
|
||||
For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
@@ -817,6 +794,35 @@ when Spring Data is on the class path.
|
||||
----
|
||||
|
||||
|
||||
[[controllers-schema-mapping-source]]
|
||||
==== Source
|
||||
|
||||
In GraphQL Java, the `DataFetchingEnvironment` provides access to the source (i.e.
|
||||
parent/container) instance of the field. To access this, simply declare a method parameter
|
||||
of the expected target type.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class BookController {
|
||||
|
||||
@SchemaMapping
|
||||
public Author author(Book book) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The source method argument also helps to determine the type name for the mapping.
|
||||
If the simple name of the Java class matches the GraphQL type, then there is no need to
|
||||
explicitly specify the type name in the `@SchemaMapping` annotation.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
A <<controllers-batch-mapping>> handler method can batch load all authors for a query,
|
||||
given a list of source/parent books objects.
|
||||
====
|
||||
|
||||
|
||||
[[controllers-schema-mapping-data-loader]]
|
||||
==== `DataLoader`
|
||||
|
||||
@@ -87,6 +87,7 @@ public class AnnotatedControllerConfigurer
|
||||
private final static boolean springDataPresent = ClassUtils.isPresent(
|
||||
"org.springframework.data.projection.SpelAwareProxyProjectionFactory",
|
||||
AnnotatedControllerConfigurer.class.getClassLoader());
|
||||
|
||||
private final static boolean springSecurityPresent = ClassUtils.isPresent(
|
||||
"org.springframework.security.core.context.SecurityContext",
|
||||
AnnotatedControllerConfigurer.class.getClassLoader());
|
||||
@@ -121,6 +122,7 @@ public class AnnotatedControllerConfigurer
|
||||
public void afterPropertiesSet() {
|
||||
this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
|
||||
if (springDataPresent) {
|
||||
// This must be ahead of ArgumentMethodArgumentResolver
|
||||
this.argumentResolvers.addResolver(new ProjectedPayloadMethodArgumentResolver());
|
||||
}
|
||||
this.argumentResolvers.addResolver(new ArgumentMapMethodArgumentResolver());
|
||||
|
||||
@@ -30,14 +30,16 @@ import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.graphql.data.method.annotation.Argument;
|
||||
|
||||
/**
|
||||
* Resolver to obtain a {@link ProjectedPayload @ProjectedPayload}
|
||||
* for {@link DataFetchingEnvironment#getArguments()}.
|
||||
* Resolver to obtain an {@link ProjectedPayload @ProjectedPayload},
|
||||
* either based on the complete {@link DataFetchingEnvironment#getArguments()}
|
||||
* map, or based on a specific argument within the map when the method
|
||||
* parameter is annotated with {@code @Argument}.
|
||||
*
|
||||
* <p>Projected payloads consist of the projection interface and accessor methods.
|
||||
* Projections can be closed or open projections. Closed projections use interface
|
||||
* getter methods to access underlying properties directly. Open projection methods
|
||||
* make use of the {@code @Value} annotation to evaluate SpEL expressions against the
|
||||
* underlying {@code target} object.
|
||||
* <p>Projected payloads consist of the projection interface and accessor
|
||||
* methods. Projections can be closed or open projections. Closed projections
|
||||
* use interface getter methods to access underlying properties directly.
|
||||
* Open projection methods make use of the {@code @Value} annotation to
|
||||
* evaluate SpEL expressions against the underlying {@code target} object.
|
||||
*
|
||||
* <p>For example:
|
||||
* <pre class="code">
|
||||
@@ -62,6 +64,17 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu
|
||||
private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.projectionFactory.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.projectionFactory.setBeanClassLoader(classLoader);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> type = parameter.getParameterType();
|
||||
@@ -88,13 +101,4 @@ public class ProjectedPayloadMethodArgumentResolver implements HandlerMethodArgu
|
||||
return this.projectionFactory.createProjection(projectionType, projectionSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.projectionFactory.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.projectionFactory.setBeanClassLoader(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class SchemaMappingInvocationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryWithProjectedArgument() {
|
||||
void queryWithProjectionOnArgumentsMap() {
|
||||
String query = "{ " +
|
||||
" booksByProjectedArguments(author:\"Orwell\") { " +
|
||||
" id" +
|
||||
@@ -116,7 +116,7 @@ public class SchemaMappingInvocationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void booksByProjectedCriteria() {
|
||||
void queryWithProjectionOnNamedArgument() {
|
||||
String query = "{ " +
|
||||
" booksByProjectedCriteria(criteria: {author:\"Orwell\"}) { " +
|
||||
" id" +
|
||||
|
||||
Reference in New Issue
Block a user