Polishing contribution

Closes gh-202
This commit is contained in:
Rossen Stoyanchev
2021-12-01 18:39:17 +00:00
parent 85cdadc2c8
commit 5597f543bd
4 changed files with 71 additions and 59 deletions

View File

@@ -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`

View File

@@ -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());

View File

@@ -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);
}
}

View File

@@ -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" +