Support documentation of a required field beneath an optional field

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
This commit is contained in:
Andy Wilkinson
2017-10-13 11:41:47 +01:00
parent ead32c3f88
commit f62b0f3ce7
2 changed files with 58 additions and 2 deletions

View File

@@ -52,8 +52,10 @@ class JsonContentHandler implements ContentHandler {
List<FieldDescriptor> 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<FieldDescriptor> fieldDescriptors, Object payload) {
List<FieldDescriptor> 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<FieldDescriptor> fieldDescriptors) {
Object content = readContent();

View File

@@ -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<FieldDescriptor> 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<FieldDescriptor> 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<FieldDescriptor> 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<FieldDescriptor> 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)));
}
}