diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java index fafde4a04..990cd2bb6 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java @@ -38,6 +38,7 @@ * * @author Andreas Evers * @author Andy Wilkinson + * @author Mathias Düsterhöft */ public abstract class AbstractFieldsSnippet extends TemplatedSnippet { @@ -160,7 +161,7 @@ protected Map createModel(Operation operation) { content = verifyContent( this.subsectionExtractor.extractSubsection(content, contentType)); } - ContentHandler contentHandler = getContentHandler(content, contentType); + ContentHandler contentHandler = ContentHandler.forContent(content, contentType); validateFieldDocumentation(contentHandler); @@ -168,7 +169,7 @@ protected Map createModel(Operation operation) { for (FieldDescriptor descriptor : this.fieldDescriptors) { if (!descriptor.isIgnored()) { try { - Object type = contentHandler.determineFieldType(descriptor); + Object type = contentHandler.resolveFieldType(descriptor); descriptorsToDocument.add(copyWithType(descriptor, type)); } catch (FieldDoesNotExistException ex) { @@ -200,36 +201,6 @@ private byte[] verifyContent(byte[] content) { return content; } - private ContentHandler getContentHandler(byte[] content, MediaType contentType) { - ContentHandler contentHandler = createJsonContentHandler(content); - if (contentHandler == null) { - contentHandler = createXmlContentHandler(content); - if (contentHandler == null) { - throw new PayloadHandlingException("Cannot handle " + contentType - + " content as it could not be parsed as JSON or XML"); - } - } - return contentHandler; - } - - private ContentHandler createJsonContentHandler(byte[] content) { - try { - return new JsonContentHandler(content); - } - catch (Exception ex) { - return null; - } - } - - private ContentHandler createXmlContentHandler(byte[] content) { - try { - return new XmlContentHandler(content); - } - catch (Exception ex) { - return null; - } - } - private void validateFieldDocumentation(ContentHandler payloadHandler) { List missingFields = payloadHandler .findMissingFields(this.fieldDescriptors); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java index 32bed30bd..9349ee88c 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java @@ -18,12 +18,15 @@ import java.util.List; +import org.springframework.http.MediaType; + /** * A handler for the content of a request or response. * * @author Andy Wilkinson + * @author Mathias Düsterhöft */ -interface ContentHandler { +interface ContentHandler extends FieldTypeResolver { /** * Finds the fields that are missing from the handler's payload. A field is missing if @@ -48,11 +51,26 @@ interface ContentHandler { String getUndocumentedContent(List fieldDescriptors); /** - * Returns the type of the field that is described by the given - * {@code fieldDescriptor} based on the content of the payload. - * @param fieldDescriptor the field descriptor - * @return the type of the field + * Create a {@link ContentHandler} for the given content type and payload. + * @param content the payload + * @param contentType the content type + * @return the ContentHandler + * @throws PayloadHandlingException if no known ContentHandler can handle the content */ - Object determineFieldType(FieldDescriptor fieldDescriptor); + static ContentHandler forContent(byte[] content, MediaType contentType) { + + try { + return new JsonContentHandler(content); + } + catch (Exception je) { + try { + return new XmlContentHandler(content); + } + catch (Exception xe) { + throw new PayloadHandlingException("Cannot handle " + contentType + + " content as it could not be parsed as JSON or XML"); + } + } + } } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java new file mode 100644 index 000000000..af7aa8596 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.payload; + +import org.springframework.http.MediaType; + +/** + * Public abstraction for external access to field type determination for xml and json + * payloads. + * + * @author Mathias Düsterhöft + * @since 2.0.3 + */ +public interface FieldTypeResolver { + + /** + * Create a FieldTypeResolver for the given content and contentType. + * @param content the payload that the {@link FieldTypeResolver} should handle + * @param contentType the content type of the payload + * @return the {@link FieldTypeResolver} + */ + static FieldTypeResolver forContent(byte[] content, MediaType contentType) { + return ContentHandler.forContent(content, contentType); + } + + /** + * Returns the type of the field that is described by the given + * {@code fieldDescriptor} based on the content of the payload. + * @param fieldDescriptor the field descriptor + * @return the type of the field + */ + Object resolveFieldType(FieldDescriptor fieldDescriptor); + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java index e64a91d11..158ec5d98 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java @@ -32,8 +32,9 @@ * A {@link ContentHandler} for JSON content. * * @author Andy Wilkinson + * @author Mathias Düsterhöft */ -class JsonContentHandler implements ContentHandler { +class JsonContentHandler implements ContentHandler, FieldTypeResolver { private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor(); @@ -145,7 +146,7 @@ private boolean isEmpty(Object object) { } @Override - public Object determineFieldType(FieldDescriptor fieldDescriptor) { + public Object resolveFieldType(FieldDescriptor fieldDescriptor) { if (fieldDescriptor.getType() == null) { return this.fieldTypeResolver.resolveFieldType(fieldDescriptor, readContent()); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java index 45c088468..c82ebb782 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java @@ -190,7 +190,7 @@ private String prettyPrint(Document document) { } @Override - public Object determineFieldType(FieldDescriptor fieldDescriptor) { + public Object resolveFieldType(FieldDescriptor fieldDescriptor) { if (fieldDescriptor.getType() != null) { return fieldDescriptor.getType(); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java new file mode 100644 index 000000000..0ed8780c4 --- /dev/null +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.payload; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.http.MediaType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link FieldTypeResolver}. + * + * @author Mathias Düsterhöft + */ +public class FieldTypeResolverTests { + + @Rule + public ExpectedException thrownException = ExpectedException.none(); + + @Test + public void returnJsonFieldTypeResolver() { + assertThat(FieldTypeResolver.forContent("{\"field\": \"value\"}".getBytes(), + MediaType.APPLICATION_JSON)).isInstanceOf(JsonContentHandler.class); + } + + @Test + public void returnXmlContentHandler() { + assertThat(FieldTypeResolver.forContent("5".getBytes(), + MediaType.APPLICATION_XML)).isInstanceOf(XmlContentHandler.class); + } + + @Test + public void throwOnInvalidContent() { + this.thrownException.expect(PayloadHandlingException.class); + FieldTypeResolver.forContent("some".getBytes(), MediaType.APPLICATION_XML); + } + +} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java index bfb6eb8fa..622bfb2a9 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java @@ -29,6 +29,7 @@ * Tests for {@link JsonContentHandler}. * * @author Andy Wilkinson + * @author Mathias Düsterhöft */ public class JsonContentHandlerTests { @@ -39,14 +40,14 @@ public class JsonContentHandlerTests { public void typeForFieldWithNullValueMustMatch() { this.thrown.expect(FieldTypesDoNotMatchException.class); new JsonContentHandler("{\"a\": null}".getBytes()) - .determineFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING)); + .resolveFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING)); } @Test public void typeForFieldWithNotNullAndThenNullValueMustMatch() { this.thrown.expect(FieldTypesDoNotMatchException.class); new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes()) - .determineFieldType( + .resolveFieldType( new FieldDescriptor("a[].id").type(JsonFieldType.STRING)); } @@ -54,7 +55,7 @@ public void typeForFieldWithNotNullAndThenNullValueMustMatch() { public void typeForFieldWithNullAndThenNotNullValueMustMatch() { this.thrown.expect(FieldTypesDoNotMatchException.class); new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes()) - .determineFieldType( + .resolveFieldType( new FieldDescriptor("a.[].id").type(JsonFieldType.STRING)); } @@ -62,7 +63,7 @@ public void typeForFieldWithNullAndThenNotNullValueMustMatch() { public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() { Object fieldType = new JsonContentHandler( "{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes()) - .determineFieldType(new FieldDescriptor("a[].id").optional()); + .resolveFieldType(new FieldDescriptor("a[].id").optional()); assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER); } @@ -70,7 +71,7 @@ public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() { public void typeForOptionalFieldWithNullAndThenNumberIsNumber() { Object fieldType = new JsonContentHandler( "{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes()) - .determineFieldType(new FieldDescriptor("a[].id").optional()); + .resolveFieldType(new FieldDescriptor("a[].id").optional()); assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER); } @@ -78,7 +79,7 @@ public void typeForOptionalFieldWithNullAndThenNumberIsNumber() { public void typeForFieldWithNumberAndThenNullValueIsVaries() { Object fieldType = new JsonContentHandler( "{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes()) - .determineFieldType(new FieldDescriptor("a[].id")); + .resolveFieldType(new FieldDescriptor("a[].id")); assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES); } @@ -86,14 +87,14 @@ public void typeForFieldWithNumberAndThenNullValueIsVaries() { public void typeForFieldWithNullAndThenNumberIsVaries() { Object fieldType = new JsonContentHandler( "{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes()) - .determineFieldType(new FieldDescriptor("a[].id")); + .resolveFieldType(new FieldDescriptor("a[].id")); assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES); } @Test public void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() { Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes()) - .determineFieldType( + .resolveFieldType( new FieldDescriptor("a").type(JsonFieldType.STRING).optional()); assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.STRING); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java index 904093b8c..511c86212 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java @@ -30,6 +30,7 @@ * Tests for {@link XmlContentHandler}. * * @author Andy Wilkinson + * @author Mathias Düsterhöft */ public class XmlContentHandlerTests {