From f62b0f3ce724915d58f867bea667ace5f4edeb1a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Oct 2017 11:41:47 +0100 Subject: [PATCH] Support documentation of a required field beneath an optional field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, if a field nested beneath an optional field was documented and was required, it would be identified as missing even if its optional ancestor was missing. This made it impossible to document required fields of an optional subsection of a payload. This commit updates JsonContentHandler to consider a field’s ancestors when determining if it should be reported as missing. If the field field has an optional ancestor that is not present, the field is not considered to be missing if it too is not present. If the field has an optional ancestor that is present then the field is considered to be missing if it is not present. Closes gh-429 --- .../restdocs/payload/JsonContentHandler.java | 20 +++++++++- .../payload/JsonContentHandlerTests.java | 40 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) 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 afacb6e1..86dbd7b4 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 @@ -52,8 +52,10 @@ class JsonContentHandler implements ContentHandler { List missingFields = new ArrayList<>(); Object payload = readContent(); for (FieldDescriptor fieldDescriptor : fieldDescriptors) { - if (!fieldDescriptor.isOptional() && !this.fieldProcessor - .hasField(fieldDescriptor.getPath(), payload)) { + if (!fieldDescriptor.isOptional() + && !this.fieldProcessor.hasField(fieldDescriptor.getPath(), payload) + && !isNestedBeneathMissingOptionalField(fieldDescriptor, + fieldDescriptors, payload)) { missingFields.add(fieldDescriptor); } } @@ -61,6 +63,20 @@ class JsonContentHandler implements ContentHandler { return missingFields; } + private boolean isNestedBeneathMissingOptionalField(FieldDescriptor missing, + List fieldDescriptors, Object payload) { + List candidates = new ArrayList<>(fieldDescriptors); + candidates.remove(missing); + for (FieldDescriptor candidate : candidates) { + if (candidate.isOptional() + && missing.getPath().startsWith(candidate.getPath()) + && !this.fieldProcessor.hasField(candidate.getPath(), payload)) { + return true; + } + } + return false; + } + @Override public String getUndocumentedContent(List fieldDescriptors) { Object content = readContent(); 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 b2e5a582..e275bd92 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 @@ -17,6 +17,8 @@ package org.springframework.restdocs.payload; import java.io.IOException; +import java.util.Arrays; +import java.util.List; import org.junit.Rule; import org.junit.Test; @@ -107,4 +109,42 @@ public class JsonContentHandlerTests { new JsonContentHandler("Non-JSON content".getBytes()); } + @Test + public void describedFieldThatIsNotPresentIsConsideredMissing() { + List missingFields = new JsonContentHandler( + "{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes()) + .findMissingFields(Arrays.asList(new FieldDescriptor("a"), + new FieldDescriptor("b"), new FieldDescriptor("c"))); + assertThat(missingFields.size(), is(equalTo(1))); + assertThat(missingFields.get(0).getPath(), is(equalTo("c"))); + } + + @Test + public void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() { + List missingFields = new JsonContentHandler( + "{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes()).findMissingFields( + Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"), + new FieldDescriptor("c").optional())); + assertThat(missingFields.size(), is(equalTo(0))); + } + + @Test + public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresentIsConsideredMissing() { + List missingFields = new JsonContentHandler( + "{\"a\":\"alpha\",\"b\":\"bravo\"}".getBytes()).findMissingFields( + Arrays.asList(new FieldDescriptor("a").optional(), + new FieldDescriptor("b"), new FieldDescriptor("a.c"))); + assertThat(missingFields.size(), is(equalTo(1))); + assertThat(missingFields.get(0).getPath(), is(equalTo("a.c"))); + } + + @Test + public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPresentIsNotConsideredMissing() { + List missingFields = new JsonContentHandler( + "{\"b\":\"bravo\"}".getBytes()).findMissingFields( + Arrays.asList(new FieldDescriptor("a").optional(), + new FieldDescriptor("b"), new FieldDescriptor("a.c"))); + assertThat(missingFields.size(), is(equalTo(0))); + } + }