Skip to content

Commit d447fe0

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

File tree

10 files changed

+189
-248
lines changed

10 files changed

+189
-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.determineFieldType(descriptor);
175173
descriptorsToDocument.add(copyWithType(descriptor, type));
176174
}
177175
catch (FieldDoesNotExistException ex) {

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

+21-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,24 @@ 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+
} catch (Exception ex) {
65+
try {
66+
return new XmlContentHandler(content);
67+
} catch (Exception e) {
68+
throw new PayloadHandlingException("Cannot handle " + contentType
69+
+ " content as it could not be parsed as JSON or XML");
70+
}
71+
}
72+
}
5773

5874
}

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

+1-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
/**
@@ -45,5 +44,4 @@ static FieldTypeResolver forContent(byte[] content, MediaType contentType) {
4544
* @return the type of the field
4645
*/
4746
Object determineFieldType(FieldDescriptor fieldDescriptor);
48-
4947
}

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

+29-6
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@
3333
*
3434
* @author Andy Wilkinson
3535
*/
36-
class JsonContentHandler implements ContentHandler {
36+
class JsonContentHandler implements ContentHandler, FieldTypeResolver {
3737

3838
private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor();
3939

40+
private final JsonFieldTypeResolver fieldTypeResolver = new JsonFieldTypeResolver();
41+
4042
private final ObjectMapper objectMapper = new ObjectMapper()
4143
.enable(SerializationFeature.INDENT_OUTPUT);
4244

@@ -122,11 +124,6 @@ public String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors) {
122124
return null;
123125
}
124126

125-
@Override
126-
public FieldTypeResolver getFieldTypeResolver() {
127-
return new JsonFieldTypeResolver(readContent());
128-
}
129-
130127
private boolean describesSubsection(FieldDescriptor fieldDescriptor) {
131128
return fieldDescriptor instanceof SubsectionDescriptor;
132129
}
@@ -147,4 +144,30 @@ private boolean isEmpty(Object object) {
147144
return ((List<?>) object).isEmpty();
148145
}
149146

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

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

+1-6
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) {

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-1
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,75 @@
2929
* Tests for {@link JsonContentHandler}.
3030
*
3131
* @author Andy Wilkinson
32-
* @author Mathias Düsterhöft
3332
*/
3433
public class JsonContentHandlerTests {
3534

3635
@Rule
3736
public ExpectedException thrown = ExpectedException.none();
3837

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

0 commit comments

Comments
 (0)