From a2a9a7cb0fe86c30016091d977aa2f7f521c96c0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Jul 2018 15:50:34 +0100 Subject: [PATCH] Stop type determination from changing descriptor's type Previously, when a field's type was automatically determined, the type of the passed-in descriptor was changed. This could cause problems if the descriptor was reused in another test. This commit updates AbstractFieldsSnippet to create a copy of each descriptor so that setting the determined type does not affect the type of the original descriptor. Closes gh-511 --- .../payload/AbstractFieldsSnippet.java | 32 +++++++++++++++++-- .../payload/RequestFieldsSnippetTests.java | 14 ++++++++ .../payload/ResponseFieldsSnippetTests.java | 14 ++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) 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 b394ce1a..f40a42a8 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 @@ -24,6 +24,8 @@ import java.util.Map; import org.springframework.http.MediaType; import org.springframework.restdocs.operation.Operation; +import org.springframework.restdocs.snippet.Attributes; +import org.springframework.restdocs.snippet.Attributes.Attribute; import org.springframework.restdocs.snippet.ModelCreationException; import org.springframework.restdocs.snippet.SnippetException; import org.springframework.restdocs.snippet.TemplatedSnippet; @@ -184,10 +186,12 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet { validateFieldDocumentation(contentHandler); + List descriptorsToDocument = new ArrayList<>(); for (FieldDescriptor descriptor : this.fieldDescriptors) { if (!descriptor.isIgnored()) { try { - descriptor.type(contentHandler.determineFieldType(descriptor)); + Object type = contentHandler.determineFieldType(descriptor); + descriptorsToDocument.add(copyWithType(descriptor, type)); } catch (FieldDoesNotExistException ex) { String message = "Cannot determine the type of the field '" @@ -202,7 +206,7 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet { Map model = new HashMap<>(); List> fields = new ArrayList<>(); model.put("fields", fields); - for (FieldDescriptor descriptor : this.fieldDescriptors) { + for (FieldDescriptor descriptor : descriptorsToDocument) { if (!descriptor.isIgnored()) { fields.add(createModelForDescriptor(descriptor)); } @@ -340,4 +344,28 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet { return model; } + private FieldDescriptor copyWithType(FieldDescriptor source, Object type) { + FieldDescriptor result = source instanceof SubsectionDescriptor + ? new SubsectionDescriptor(source.getPath()) + : new FieldDescriptor(source.getPath()); + result.description(source.getDescription()).type(type) + .attributes(asArray(source.getAttributes())); + if (source.isIgnored()) { + result.ignored(); + } + if (source.isOptional()) { + result.optional(); + } + return result; + } + + private static Attribute[] asArray(Map attributeMap) { + List attributes = new ArrayList<>(); + for (Map.Entry attribute : attributeMap.entrySet()) { + attributes + .add(Attributes.key(attribute.getKey()).value(attribute.getValue())); + } + return attributes.toArray(new Attribute[attributes.size()]); + } + } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java index ffb4d4dc..43eece49 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java @@ -31,6 +31,9 @@ import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; @@ -446,6 +449,17 @@ public class RequestFieldsSnippetTests extends AbstractSnippetTests { .build()); } + @Test + public void typeDeterminationDoesNotSetTypeOnDescriptor() throws IOException { + this.snippets.expectRequestFields() + .withContents(tableWithHeader("Path", "Type", "Description").row("`a.b`", + "`Number`", "one")); + FieldDescriptor descriptor = fieldWithPath("a.b").description("one"); + new RequestFieldsSnippet(Arrays.asList(descriptor)).document(this.operationBuilder + .request("http://localhost").content("{\"a\": {\"b\": 5}}").build()); + assertThat(descriptor.getType(), is(nullValue())); + } + private String escapeIfNecessary(String input) { if (this.templateFormat.equals(TemplateFormats.markdown())) { return input; diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java index cd6fdb52..020131df 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java @@ -31,6 +31,9 @@ import org.springframework.restdocs.templates.TemplateResourceResolver; import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; @@ -457,6 +460,17 @@ public class ResponseFieldsSnippetTests extends AbstractSnippetTests { .build()); } + @Test + public void typeDeterminationDoesNotSetTypeOnDescriptor() throws IOException { + this.snippets.expectResponseFields() + .withContents(tableWithHeader("Path", "Type", "Description").row("`id`", + "`Number`", "one")); + FieldDescriptor descriptor = fieldWithPath("id").description("one"); + new ResponseFieldsSnippet(Arrays.asList(descriptor)).document( + this.operationBuilder.response().content("{\"id\": 67}").build()); + assertThat(descriptor.getType(), is(nullValue())); + } + private String escapeIfNecessary(String input) { if (this.templateFormat.equals(TemplateFormats.markdown())) { return input;