Skip to content

Commit 13683ee

Browse files
committed
Support Optional for ProjectPayload arguments
Closes gh-506
1 parent 560c2b5 commit 13683ee

File tree

2 files changed

+113
-9
lines changed

2 files changed

+113
-9
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ProjectedPayloadMethodArgumentResolver.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616
package org.springframework.graphql.data.method.annotation.support;
1717

1818

19+
import java.util.Optional;
20+
1921
import graphql.schema.DataFetchingEnvironment;
2022

2123
import org.springframework.context.ApplicationContext;
@@ -77,24 +79,33 @@ public ProjectedPayloadMethodArgumentResolver(ApplicationContext applicationCont
7779

7880
@Override
7981
public boolean supportsParameter(MethodParameter parameter) {
80-
Class<?> type = parameter.getParameterType();
81-
82-
if (!type.isInterface()) {
83-
return false;
84-
}
85-
86-
return AnnotatedElementUtils.findMergedAnnotation(type, ProjectedPayload.class) != null;
82+
Class<?> type = parameter.nestedIfOptional().getNestedParameterType();
83+
return (type.isInterface() &&
84+
AnnotatedElementUtils.findMergedAnnotation(type, ProjectedPayload.class) != null);
8785
}
8886

8987
@Override
9088
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
89+
9190
String name = (parameter.hasParameterAnnotation(Argument.class) ?
9291
ArgumentMethodArgumentResolver.getArgumentName(parameter) : null);
9392

93+
Class<?> projectionType = parameter.getParameterType();
94+
95+
boolean isOptional = parameter.isOptional();
96+
if (isOptional) {
97+
projectionType = parameter.nestedIfOptional().getNestedParameterType();
98+
}
99+
94100
Object projectionSource = (name != null ?
95101
environment.getArgument(name) : environment.getArguments());
96102

97-
return project(parameter.getParameterType(), projectionSource);
103+
Object value = null;
104+
if (!isOptional || projectionSource != null) {
105+
value = project(projectionType, projectionSource);
106+
}
107+
108+
return (isOptional ? Optional.ofNullable(value) : value);
98109
}
99110

100111
protected Object project(Class<?> projectionType, Object projectionSource){
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.graphql.data.method.annotation.support;
17+
18+
import java.util.List;
19+
import java.util.Optional;
20+
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.context.support.StaticApplicationContext;
25+
import org.springframework.core.MethodParameter;
26+
import org.springframework.data.web.ProjectedPayload;
27+
import org.springframework.graphql.Book;
28+
import org.springframework.graphql.data.method.annotation.Argument;
29+
import org.springframework.graphql.data.method.annotation.QueryMapping;
30+
import org.springframework.stereotype.Controller;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
/**
35+
*
36+
*/
37+
public class ProjectedPayloadMethodArgumentResolverTests extends ArgumentResolverTestSupport {
38+
39+
private ProjectedPayloadMethodArgumentResolver resolver;
40+
41+
42+
@BeforeEach
43+
void setUp() {
44+
StaticApplicationContext context = new StaticApplicationContext();
45+
this.resolver = new ProjectedPayloadMethodArgumentResolver(context);
46+
}
47+
48+
49+
@Test
50+
void supports() {
51+
MethodParameter param = methodParam(BookController.class, "optionalProjection", Optional.class);
52+
assertThat(this.resolver.supportsParameter(param)).isTrue();
53+
54+
param = methodParam(BookController.class, "optionalString", Optional.class);
55+
assertThat(this.resolver.supportsParameter(param)).isFalse();
56+
}
57+
58+
@Test
59+
void optionalWrapper() throws Exception {
60+
61+
Object result = this.resolver.resolveArgument(
62+
methodParam(BookController.class, "optionalProjection", Optional.class),
63+
environment("{}"));
64+
65+
assertThat(result).isNotNull().isInstanceOf(Optional.class);
66+
assertThat((Optional<?>) result).isNotPresent();
67+
}
68+
69+
70+
@SuppressWarnings({"ConstantConditions", "unused"})
71+
@Controller
72+
static class BookController {
73+
74+
@QueryMapping
75+
public List<Book> optionalProjection(@Argument(name = "where") Optional<BookProjection> projection) {
76+
return null;
77+
}
78+
79+
@QueryMapping
80+
public void optionalString(@Argument Optional<String> projection) {
81+
}
82+
83+
}
84+
85+
86+
@ProjectedPayload
87+
interface BookProjection {
88+
89+
String getAuthor();
90+
91+
}
92+
93+
}

0 commit comments

Comments
 (0)