Skip to content

Commit f62b0f3

Browse files
committed
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
1 parent ead32c3 commit f62b0f3

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,31 @@ public List<FieldDescriptor> findMissingFields(
5252
List<FieldDescriptor> missingFields = new ArrayList<>();
5353
Object payload = readContent();
5454
for (FieldDescriptor fieldDescriptor : fieldDescriptors) {
55-
if (!fieldDescriptor.isOptional() && !this.fieldProcessor
56-
.hasField(fieldDescriptor.getPath(), payload)) {
55+
if (!fieldDescriptor.isOptional()
56+
&& !this.fieldProcessor.hasField(fieldDescriptor.getPath(), payload)
57+
&& !isNestedBeneathMissingOptionalField(fieldDescriptor,
58+
fieldDescriptors, payload)) {
5759
missingFields.add(fieldDescriptor);
5860
}
5961
}
6062

6163
return missingFields;
6264
}
6365

66+
private boolean isNestedBeneathMissingOptionalField(FieldDescriptor missing,
67+
List<FieldDescriptor> fieldDescriptors, Object payload) {
68+
List<FieldDescriptor> candidates = new ArrayList<>(fieldDescriptors);
69+
candidates.remove(missing);
70+
for (FieldDescriptor candidate : candidates) {
71+
if (candidate.isOptional()
72+
&& missing.getPath().startsWith(candidate.getPath())
73+
&& !this.fieldProcessor.hasField(candidate.getPath(), payload)) {
74+
return true;
75+
}
76+
}
77+
return false;
78+
}
79+
6480
@Override
6581
public String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors) {
6682
Object content = readContent();

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.restdocs.payload;
1818

1919
import java.io.IOException;
20+
import java.util.Arrays;
21+
import java.util.List;
2022

2123
import org.junit.Rule;
2224
import org.junit.Test;
@@ -107,4 +109,42 @@ public void failsFastWithNonJsonContent() {
107109
new JsonContentHandler("Non-JSON content".getBytes());
108110
}
109111

112+
@Test
113+
public void describedFieldThatIsNotPresentIsConsideredMissing() {
114+
List<FieldDescriptor> missingFields = new JsonContentHandler(
115+
"{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes())
116+
.findMissingFields(Arrays.asList(new FieldDescriptor("a"),
117+
new FieldDescriptor("b"), new FieldDescriptor("c")));
118+
assertThat(missingFields.size(), is(equalTo(1)));
119+
assertThat(missingFields.get(0).getPath(), is(equalTo("c")));
120+
}
121+
122+
@Test
123+
public void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() {
124+
List<FieldDescriptor> missingFields = new JsonContentHandler(
125+
"{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes()).findMissingFields(
126+
Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"),
127+
new FieldDescriptor("c").optional()));
128+
assertThat(missingFields.size(), is(equalTo(0)));
129+
}
130+
131+
@Test
132+
public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresentIsConsideredMissing() {
133+
List<FieldDescriptor> missingFields = new JsonContentHandler(
134+
"{\"a\":\"alpha\",\"b\":\"bravo\"}".getBytes()).findMissingFields(
135+
Arrays.asList(new FieldDescriptor("a").optional(),
136+
new FieldDescriptor("b"), new FieldDescriptor("a.c")));
137+
assertThat(missingFields.size(), is(equalTo(1)));
138+
assertThat(missingFields.get(0).getPath(), is(equalTo("a.c")));
139+
}
140+
141+
@Test
142+
public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPresentIsNotConsideredMissing() {
143+
List<FieldDescriptor> missingFields = new JsonContentHandler(
144+
"{\"b\":\"bravo\"}".getBytes()).findMissingFields(
145+
Arrays.asList(new FieldDescriptor("a").optional(),
146+
new FieldDescriptor("b"), new FieldDescriptor("a.c")));
147+
assertThat(missingFields.size(), is(equalTo(0)));
148+
}
149+
110150
}

0 commit comments

Comments
 (0)