Skip to content

Commit 4453fde

Browse files
Mathias Düsterhöftwilkinsona
Mathias Düsterhöft
authored andcommitted
Provide a public API for resolving the type of a field
See gh-549
1 parent ada273b commit 4453fde

File tree

8 files changed

+144
-49
lines changed

8 files changed

+144
-49
lines changed

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

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
*
3939
* @author Andreas Evers
4040
* @author Andy Wilkinson
41+
* @author Mathias Düsterhöft
4142
*/
4243
public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
4344

@@ -160,15 +161,15 @@ protected Map<String, Object> createModel(Operation operation) {
160161
content = verifyContent(
161162
this.subsectionExtractor.extractSubsection(content, contentType));
162163
}
163-
ContentHandler contentHandler = getContentHandler(content, contentType);
164+
ContentHandler contentHandler = ContentHandler.forContent(content, contentType);
164165

165166
validateFieldDocumentation(contentHandler);
166167

167168
List<FieldDescriptor> descriptorsToDocument = new ArrayList<>();
168169
for (FieldDescriptor descriptor : this.fieldDescriptors) {
169170
if (!descriptor.isIgnored()) {
170171
try {
171-
Object type = contentHandler.determineFieldType(descriptor);
172+
Object type = contentHandler.resolveFieldType(descriptor);
172173
descriptorsToDocument.add(copyWithType(descriptor, type));
173174
}
174175
catch (FieldDoesNotExistException ex) {
@@ -200,36 +201,6 @@ private byte[] verifyContent(byte[] content) {
200201
return content;
201202
}
202203

203-
private ContentHandler getContentHandler(byte[] content, MediaType contentType) {
204-
ContentHandler contentHandler = createJsonContentHandler(content);
205-
if (contentHandler == null) {
206-
contentHandler = createXmlContentHandler(content);
207-
if (contentHandler == null) {
208-
throw new PayloadHandlingException("Cannot handle " + contentType
209-
+ " content as it could not be parsed as JSON or XML");
210-
}
211-
}
212-
return contentHandler;
213-
}
214-
215-
private ContentHandler createJsonContentHandler(byte[] content) {
216-
try {
217-
return new JsonContentHandler(content);
218-
}
219-
catch (Exception ex) {
220-
return null;
221-
}
222-
}
223-
224-
private ContentHandler createXmlContentHandler(byte[] content) {
225-
try {
226-
return new XmlContentHandler(content);
227-
}
228-
catch (Exception ex) {
229-
return null;
230-
}
231-
}
232-
233204
private void validateFieldDocumentation(ContentHandler payloadHandler) {
234205
List<FieldDescriptor> missingFields = payloadHandler
235206
.findMissingFields(this.fieldDescriptors);

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +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
27+
* @author Mathias Düsterhöft
2528
*/
26-
interface ContentHandler {
29+
interface ContentHandler extends FieldTypeResolver {
2730

2831
/**
2932
* Finds the fields that are missing from the handler's payload. A field is missing if
@@ -48,11 +51,26 @@ interface ContentHandler {
4851
String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors);
4952

5053
/**
51-
* Returns the type of the field that is described by the given
52-
* {@code fieldDescriptor} based on the content of the payload.
53-
* @param fieldDescriptor the field descriptor
54-
* @return the type of the field
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-
Object determineFieldType(FieldDescriptor fieldDescriptor);
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
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2014-2018 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+
* http://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+
17+
package org.springframework.restdocs.payload;
18+
19+
import org.springframework.http.MediaType;
20+
21+
/**
22+
* Public abstraction for external access to field type determination for xml and json
23+
* payloads.
24+
*
25+
* @author Mathias Düsterhöft
26+
* @since 2.0.3
27+
*/
28+
public interface FieldTypeResolver {
29+
30+
/**
31+
* Create a FieldTypeResolver for the given content and contentType.
32+
* @param content the payload that the {@link FieldTypeResolver} should handle
33+
* @param contentType the content type of the payload
34+
* @return the {@link FieldTypeResolver}
35+
*/
36+
static FieldTypeResolver forContent(byte[] content, MediaType contentType) {
37+
return ContentHandler.forContent(content, contentType);
38+
}
39+
40+
/**
41+
* Returns the type of the field that is described by the given
42+
* {@code fieldDescriptor} based on the content of the payload.
43+
* @param fieldDescriptor the field descriptor
44+
* @return the type of the field
45+
*/
46+
Object resolveFieldType(FieldDescriptor fieldDescriptor);
47+
48+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
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

@@ -145,7 +146,7 @@ private boolean isEmpty(Object object) {
145146
}
146147

147148
@Override
148-
public Object determineFieldType(FieldDescriptor fieldDescriptor) {
149+
public Object resolveFieldType(FieldDescriptor fieldDescriptor) {
149150
if (fieldDescriptor.getType() == null) {
150151
return this.fieldTypesDiscoverer
151152
.discoverFieldTypes(fieldDescriptor.getPath(), readContent())

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ private String prettyPrint(Document document) {
190190
}
191191

192192
@Override
193-
public Object determineFieldType(FieldDescriptor fieldDescriptor) {
193+
public Object resolveFieldType(FieldDescriptor fieldDescriptor) {
194194
if (fieldDescriptor.getType() != null) {
195195
return fieldDescriptor.getType();
196196
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2014-2018 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+
* http://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+
17+
package org.springframework.restdocs.payload;
18+
19+
import org.junit.Rule;
20+
import org.junit.Test;
21+
import org.junit.rules.ExpectedException;
22+
23+
import org.springframework.http.MediaType;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
/**
28+
* Tests for {@link FieldTypeResolver}.
29+
*
30+
* @author Mathias Düsterhöft
31+
*/
32+
public class FieldTypeResolverTests {
33+
34+
@Rule
35+
public ExpectedException thrownException = ExpectedException.none();
36+
37+
@Test
38+
public void returnJsonFieldTypeResolver() {
39+
assertThat(FieldTypeResolver.forContent("{\"field\": \"value\"}".getBytes(),
40+
MediaType.APPLICATION_JSON)).isInstanceOf(JsonContentHandler.class);
41+
}
42+
43+
@Test
44+
public void returnXmlContentHandler() {
45+
assertThat(FieldTypeResolver.forContent("<a><b>5</b></a>".getBytes(),
46+
MediaType.APPLICATION_XML)).isInstanceOf(XmlContentHandler.class);
47+
}
48+
49+
@Test
50+
public void throwOnInvalidContent() {
51+
this.thrownException.expect(PayloadHandlingException.class);
52+
FieldTypeResolver.forContent("some".getBytes(), MediaType.APPLICATION_XML);
53+
}
54+
55+
}

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* Tests for {@link JsonContentHandler}.
3030
*
3131
* @author Andy Wilkinson
32+
* @author Mathias Düsterhöft
3233
*/
3334
public class JsonContentHandlerTests {
3435

@@ -39,61 +40,61 @@ public class JsonContentHandlerTests {
3940
public void typeForFieldWithNullValueMustMatch() {
4041
this.thrown.expect(FieldTypesDoNotMatchException.class);
4142
new JsonContentHandler("{\"a\": null}".getBytes())
42-
.determineFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING));
43+
.resolveFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING));
4344
}
4445

4546
@Test
4647
public void typeForFieldWithNotNullAndThenNullValueMustMatch() {
4748
this.thrown.expect(FieldTypesDoNotMatchException.class);
4849
new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes())
49-
.determineFieldType(
50+
.resolveFieldType(
5051
new FieldDescriptor("a[].id").type(JsonFieldType.STRING));
5152
}
5253

5354
@Test
5455
public void typeForFieldWithNullAndThenNotNullValueMustMatch() {
5556
this.thrown.expect(FieldTypesDoNotMatchException.class);
5657
new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
57-
.determineFieldType(
58+
.resolveFieldType(
5859
new FieldDescriptor("a.[].id").type(JsonFieldType.STRING));
5960
}
6061

6162
@Test
6263
public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() {
6364
Object fieldType = new JsonContentHandler(
6465
"{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes())
65-
.determineFieldType(new FieldDescriptor("a[].id").optional());
66+
.resolveFieldType(new FieldDescriptor("a[].id").optional());
6667
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
6768
}
6869

6970
@Test
7071
public void typeForOptionalFieldWithNullAndThenNumberIsNumber() {
7172
Object fieldType = new JsonContentHandler(
7273
"{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
73-
.determineFieldType(new FieldDescriptor("a[].id").optional());
74+
.resolveFieldType(new FieldDescriptor("a[].id").optional());
7475
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
7576
}
7677

7778
@Test
7879
public void typeForFieldWithNumberAndThenNullValueIsVaries() {
7980
Object fieldType = new JsonContentHandler(
8081
"{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes())
81-
.determineFieldType(new FieldDescriptor("a[].id"));
82+
.resolveFieldType(new FieldDescriptor("a[].id"));
8283
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES);
8384
}
8485

8586
@Test
8687
public void typeForFieldWithNullAndThenNumberIsVaries() {
8788
Object fieldType = new JsonContentHandler(
8889
"{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
89-
.determineFieldType(new FieldDescriptor("a[].id"));
90+
.resolveFieldType(new FieldDescriptor("a[].id"));
9091
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES);
9192
}
9293

9394
@Test
9495
public void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() {
9596
Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes())
96-
.determineFieldType(
97+
.resolveFieldType(
9798
new FieldDescriptor("a").type(JsonFieldType.STRING).optional());
9899
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.STRING);
99100
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* Tests for {@link XmlContentHandler}.
3131
*
3232
* @author Andy Wilkinson
33+
* @author Mathias Düsterhöft
3334
*/
3435
public class XmlContentHandlerTests {
3536

0 commit comments

Comments
 (0)