Skip to content

Commit bc96ffd

Browse files
author
Mathias Düsterhöft
committed
Keep separation of concerns of JsonContentHandler and JsonFieldTypeResolver.
1 parent 61fe2fd commit bc96ffd

11 files changed

+195
-248
lines changed

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -161,17 +161,15 @@ protected Map<String, Object> createModel(Operation operation) {
161161
content = verifyContent(
162162
this.subsectionExtractor.extractSubsection(content, contentType));
163163
}
164-
ContentHandler contentHandler = ContentTypeHandlerFactory.create(content,
165-
contentType);
164+
ContentHandler contentHandler = ContentHandler.forContent(content, contentType);
166165

167166
validateFieldDocumentation(contentHandler);
168167

169168
List<FieldDescriptor> descriptorsToDocument = new ArrayList<>();
170169
for (FieldDescriptor descriptor : this.fieldDescriptors) {
171170
if (!descriptor.isIgnored()) {
172171
try {
173-
Object type = contentHandler.getFieldTypeResolver()
174-
.determineFieldType(descriptor);
172+
Object type = contentHandler.resolveFieldType(descriptor);
175173
descriptorsToDocument.add(copyWithType(descriptor, type));
176174
}
177175
catch (FieldDoesNotExistException ex) {

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java

+23-5
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818

1919
import java.util.List;
2020

21+
import org.springframework.http.MediaType;
22+
2123
/**
2224
* A handler for the content of a request or response.
2325
*
2426
* @author Andy Wilkinson
2527
* @author Mathias Düsterhöft
2628
*/
27-
interface ContentHandler {
29+
interface ContentHandler extends FieldTypeResolver {
2830

2931
/**
3032
* Finds the fields that are missing from the handler's payload. A field is missing if
@@ -49,10 +51,26 @@ interface ContentHandler {
4951
String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors);
5052

5153
/**
52-
* Return a {@link FieldTypeResolver} that can be used for the content type this
53-
* ContentHandler can process.
54-
* @return a {@link FieldTypeResolver}
54+
* Create a {@link ContentHandler} for the given content type and payload.
55+
* @param content the payload
56+
* @param contentType the content type
57+
* @return the ContentHandler
58+
* @throws PayloadHandlingException if no known ContentHandler can handle the content
5559
*/
56-
FieldTypeResolver getFieldTypeResolver();
60+
static ContentHandler forContent(byte[] content, MediaType contentType) {
61+
62+
try {
63+
return new JsonContentHandler(content);
64+
}
65+
catch (Exception je) {
66+
try {
67+
return new XmlContentHandler(content);
68+
}
69+
catch (Exception xe) {
70+
throw new PayloadHandlingException("Cannot handle " + contentType
71+
+ " content as it could not be parsed as JSON or XML");
72+
}
73+
}
74+
}
5775

5876
}

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentTypeHandlerFactory.java

-67
This file was deleted.

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ public interface FieldTypeResolver {
3434
* @return the {@link FieldTypeResolver}
3535
*/
3636
static FieldTypeResolver forContent(byte[] content, MediaType contentType) {
37-
return ContentTypeHandlerFactory.create(content, contentType)
38-
.getFieldTypeResolver();
37+
return ContentHandler.forContent(content, contentType);
3938
}
4039

4140
/**
@@ -44,6 +43,6 @@ static FieldTypeResolver forContent(byte[] content, MediaType contentType) {
4443
* @param fieldDescriptor the field descriptor
4544
* @return the type of the field
4645
*/
47-
Object determineFieldType(FieldDescriptor fieldDescriptor);
46+
Object resolveFieldType(FieldDescriptor fieldDescriptor);
4847

4948
}

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java

+30-6
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,14 @@
3232
* A {@link ContentHandler} for JSON content.
3333
*
3434
* @author Andy Wilkinson
35+
* @author Mathias Düsterhöft
3536
*/
36-
class JsonContentHandler implements ContentHandler {
37+
class JsonContentHandler implements ContentHandler, FieldTypeResolver {
3738

3839
private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor();
3940

41+
private final JsonFieldTypeResolver fieldTypeResolver = new JsonFieldTypeResolver();
42+
4043
private final ObjectMapper objectMapper = new ObjectMapper()
4144
.enable(SerializationFeature.INDENT_OUTPUT);
4245

@@ -122,11 +125,6 @@ public String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors) {
122125
return null;
123126
}
124127

125-
@Override
126-
public FieldTypeResolver getFieldTypeResolver() {
127-
return new JsonFieldTypeResolver(readContent());
128-
}
129-
130128
private boolean describesSubsection(FieldDescriptor fieldDescriptor) {
131129
return fieldDescriptor instanceof SubsectionDescriptor;
132130
}
@@ -147,4 +145,30 @@ private boolean isEmpty(Object object) {
147145
return ((List<?>) object).isEmpty();
148146
}
149147

148+
@Override
149+
public Object resolveFieldType(FieldDescriptor fieldDescriptor) {
150+
if (fieldDescriptor.getType() == null) {
151+
return this.fieldTypeResolver.resolveFieldType(fieldDescriptor,
152+
readContent());
153+
}
154+
if (!(fieldDescriptor.getType() instanceof JsonFieldType)) {
155+
return fieldDescriptor.getType();
156+
}
157+
JsonFieldType descriptorFieldType = (JsonFieldType) fieldDescriptor.getType();
158+
try {
159+
JsonFieldType actualFieldType = this.fieldTypeResolver
160+
.resolveFieldType(fieldDescriptor, readContent());
161+
if (descriptorFieldType == JsonFieldType.VARIES
162+
|| descriptorFieldType == actualFieldType
163+
|| (fieldDescriptor.isOptional()
164+
&& actualFieldType == JsonFieldType.NULL)) {
165+
return descriptorFieldType;
166+
}
167+
throw new FieldTypesDoNotMatchException(fieldDescriptor, actualFieldType);
168+
}
169+
catch (FieldDoesNotExistException ex) {
170+
return fieldDescriptor.getType();
171+
}
172+
}
173+
150174
}

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypeResolver.java

+3-34
Original file line numberDiff line numberDiff line change
@@ -26,45 +26,14 @@
2626
* Resolves the type of a field in a JSON request or response payload.
2727
*
2828
* @author Andy Wilkinson
29-
* @author Mathias Düsterhöft
3029
*/
31-
class JsonFieldTypeResolver implements FieldTypeResolver {
32-
33-
private final Object content;
30+
class JsonFieldTypeResolver {
3431

3532
private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor();
3633

37-
JsonFieldTypeResolver(Object content) {
38-
this.content = content;
39-
}
40-
41-
@Override
42-
public Object determineFieldType(FieldDescriptor fieldDescriptor) {
43-
if (fieldDescriptor.getType() == null) {
44-
return resolveFieldType(fieldDescriptor);
45-
}
46-
if (!(fieldDescriptor.getType() instanceof JsonFieldType)) {
47-
return fieldDescriptor.getType();
48-
}
49-
JsonFieldType descriptorFieldType = (JsonFieldType) fieldDescriptor.getType();
50-
try {
51-
JsonFieldType actualFieldType = resolveFieldType(fieldDescriptor);
52-
if (descriptorFieldType == JsonFieldType.VARIES
53-
|| descriptorFieldType == actualFieldType
54-
|| (fieldDescriptor.isOptional()
55-
&& actualFieldType == JsonFieldType.NULL)) {
56-
return descriptorFieldType;
57-
}
58-
throw new FieldTypesDoNotMatchException(fieldDescriptor, actualFieldType);
59-
}
60-
catch (FieldDoesNotExistException ex) {
61-
return fieldDescriptor.getType();
62-
}
63-
}
64-
65-
JsonFieldType resolveFieldType(FieldDescriptor fieldDescriptor) {
34+
JsonFieldType resolveFieldType(FieldDescriptor fieldDescriptor, Object payload) {
6635
ExtractedField extractedField = this.fieldProcessor
67-
.extract(fieldDescriptor.getPath(), this.content);
36+
.extract(fieldDescriptor.getPath(), payload);
6837
Object value = extractedField.getValue();
6938
if (value instanceof Collection && extractedField.getType() == PathType.MULTI) {
7039
JsonFieldType commonType = null;

spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
*
4848
* @author Andy Wilkinson
4949
*/
50-
class XmlContentHandler implements ContentHandler, FieldTypeResolver {
50+
class XmlContentHandler implements ContentHandler {
5151

5252
private final DocumentBuilder documentBuilder;
5353

@@ -146,11 +146,6 @@ public String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors) {
146146
return null;
147147
}
148148

149-
@Override
150-
public FieldTypeResolver getFieldTypeResolver() {
151-
return this;
152-
}
153-
154149
private void removeLeafNodes(List<Node> candidates) {
155150
boolean changed = true;
156151
while (changed) {
@@ -195,7 +190,7 @@ private String prettyPrint(Document document) {
195190
}
196191

197192
@Override
198-
public Object determineFieldType(FieldDescriptor fieldDescriptor) {
193+
public Object resolveFieldType(FieldDescriptor fieldDescriptor) {
199194
if (fieldDescriptor.getType() != null) {
200195
return fieldDescriptor.getType();
201196
}

spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class FieldTypeResolverTests {
3737
@Test
3838
public void returnJsonFieldTypeResolver() {
3939
assertThat(FieldTypeResolver.forContent("{\"field\": \"value\"}".getBytes(),
40-
MediaType.APPLICATION_JSON)).isInstanceOf(JsonFieldTypeResolver.class);
40+
MediaType.APPLICATION_JSON)).isInstanceOf(JsonContentHandler.class);
4141
}
4242

4343
@Test

spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java

+63
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,69 @@ public class JsonContentHandlerTests {
3636
@Rule
3737
public ExpectedException thrown = ExpectedException.none();
3838

39+
@Test
40+
public void typeForFieldWithNullValueMustMatch() {
41+
this.thrown.expect(FieldTypesDoNotMatchException.class);
42+
new JsonContentHandler("{\"a\": null}".getBytes())
43+
.resolveFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING));
44+
}
45+
46+
@Test
47+
public void typeForFieldWithNotNullAndThenNullValueMustMatch() {
48+
this.thrown.expect(FieldTypesDoNotMatchException.class);
49+
new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes())
50+
.resolveFieldType(
51+
new FieldDescriptor("a[].id").type(JsonFieldType.STRING));
52+
}
53+
54+
@Test
55+
public void typeForFieldWithNullAndThenNotNullValueMustMatch() {
56+
this.thrown.expect(FieldTypesDoNotMatchException.class);
57+
new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
58+
.resolveFieldType(
59+
new FieldDescriptor("a.[].id").type(JsonFieldType.STRING));
60+
}
61+
62+
@Test
63+
public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() {
64+
Object fieldType = new JsonContentHandler(
65+
"{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes())
66+
.resolveFieldType(new FieldDescriptor("a[].id").optional());
67+
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
68+
}
69+
70+
@Test
71+
public void typeForOptionalFieldWithNullAndThenNumberIsNumber() {
72+
Object fieldType = new JsonContentHandler(
73+
"{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
74+
.resolveFieldType(new FieldDescriptor("a[].id").optional());
75+
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
76+
}
77+
78+
@Test
79+
public void typeForFieldWithNumberAndThenNullValueIsVaries() {
80+
Object fieldType = new JsonContentHandler(
81+
"{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes())
82+
.resolveFieldType(new FieldDescriptor("a[].id"));
83+
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES);
84+
}
85+
86+
@Test
87+
public void typeForFieldWithNullAndThenNumberIsVaries() {
88+
Object fieldType = new JsonContentHandler(
89+
"{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
90+
.resolveFieldType(new FieldDescriptor("a[].id"));
91+
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES);
92+
}
93+
94+
@Test
95+
public void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() {
96+
Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes())
97+
.resolveFieldType(
98+
new FieldDescriptor("a").type(JsonFieldType.STRING).optional());
99+
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.STRING);
100+
}
101+
39102
@Test
40103
public void failsFastWithNonJsonContent() {
41104
this.thrown.expect(PayloadHandlingException.class);

0 commit comments

Comments
 (0)