diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java
index f5493128..95f70b3b 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java
@@ -66,6 +66,26 @@ public abstract class HypermediaDocumentation {
Arrays.asList(descriptors));
}
+ /**
+ * Returns a new {@code Snippet} that will document the links in the API operation's
+ * response. Links will be extracted from the response automatically based on its
+ * content type and will be documented using the given {@code descriptors}.
+ *
+ * If a link is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented links will be ignored.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
+ *
+ * @param descriptors the descriptions of the response's links
+ * @return the snippet that will document the links
+ */
+ public static LinksSnippet relaxedLinks(LinkDescriptor... descriptors) {
+ return new LinksSnippet(new ContentTypeLinkExtractor(),
+ Arrays.asList(descriptors), true);
+ }
+
/**
* Returns a new {@code Snippet} that will document the links in the API call's
* response. The given {@code attributes} will be available during snippet generation.
@@ -80,6 +100,10 @@ public abstract class HypermediaDocumentation {
* If you do not want to document a link, a link descriptor can be marked as
* {@link LinkDescriptor#ignored}. This will prevent it from appearing in the
* generated snippet while avoiding the failure described above.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
*
* @param attributes the attributes
* @param descriptors the descriptions of the response's links
@@ -91,6 +115,29 @@ public abstract class HypermediaDocumentation {
Arrays.asList(descriptors), attributes);
}
+ /**
+ * Returns a new {@code Snippet} that will document the links in the API call's
+ * response. The given {@code attributes} will be available during snippet generation.
+ * Links will be extracted from the response automatically based on its content type
+ * and will be documented using the given {@code descriptors}.
+ *
+ * If a link is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented links will be ignored.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
+ *
+ * @param attributes the attributes
+ * @param descriptors the descriptions of the response's links
+ * @return the snippet that will document the links
+ */
+ public static LinksSnippet relaxedLinks(Map attributes,
+ LinkDescriptor... descriptors) {
+ return new LinksSnippet(new ContentTypeLinkExtractor(),
+ Arrays.asList(descriptors), attributes, true);
+ }
+
/**
* Returns a new {@code Snippet} that will document the links in the API operation's
* response. Links will be extracted from the response using the given
@@ -104,6 +151,10 @@ public abstract class HypermediaDocumentation {
* If you do not want to document a link, a link descriptor can be marked as
* {@link LinkDescriptor#ignored}. This will prevent it from appearing in the
* generated snippet while avoiding the failure described above.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
*
* @param linkExtractor used to extract the links from the response
* @param descriptors the descriptions of the response's links
@@ -114,6 +165,27 @@ public abstract class HypermediaDocumentation {
return new LinksSnippet(linkExtractor, Arrays.asList(descriptors));
}
+ /**
+ * Returns a new {@code Snippet} that will document the links in the API operation's
+ * response. Links will be extracted from the response using the given
+ * {@code linkExtractor} and will be documented using the given {@code descriptors}.
+ *
+ * If a link is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented links will be ignored.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
+ *
+ * @param linkExtractor used to extract the links from the response
+ * @param descriptors the descriptions of the response's links
+ * @return the snippet that will document the links
+ */
+ public static LinksSnippet relaxedLinks(LinkExtractor linkExtractor,
+ LinkDescriptor... descriptors) {
+ return new LinksSnippet(linkExtractor, Arrays.asList(descriptors), true);
+ }
+
/**
* Returns a new {@code Snippet} that will document the links in the API operation's
* response. The given {@code attributes} will be available during snippet generation.
@@ -128,6 +200,10 @@ public abstract class HypermediaDocumentation {
* If you do not want to document a link, a link descriptor can be marked as
* {@link LinkDescriptor#ignored}. This will prevent it from appearing in the
* generated snippet while avoiding the failure described above.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
*
* @param attributes the attributes
* @param linkExtractor used to extract the links from the response
@@ -139,6 +215,30 @@ public abstract class HypermediaDocumentation {
return new LinksSnippet(linkExtractor, Arrays.asList(descriptors), attributes);
}
+ /**
+ * Returns a new {@code Snippet} that will document the links in the API operation's
+ * response. The given {@code attributes} will be available during snippet generation.
+ * Links will be extracted from the response using the given {@code linkExtractor} and
+ * will be documented using the given {@code descriptors}.
+ *
+ * If a link is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented links will be ignored.
+ *
+ * If a descriptor does not have a {@link LinkDescriptor#description(Object)
+ * description}, the {@link Link#getTitle() title} of the link will be used. If the
+ * link does not have a title a failure will occur.
+ *
+ * @param attributes the attributes
+ * @param linkExtractor used to extract the links from the response
+ * @param descriptors the descriptions of the response's links
+ * @return the snippet that will document the links
+ */
+ public static LinksSnippet relaxedLinks(LinkExtractor linkExtractor,
+ Map attributes, LinkDescriptor... descriptors) {
+ return new LinksSnippet(linkExtractor, Arrays.asList(descriptors), attributes,
+ true);
+ }
+
/**
* Returns a {@code LinkExtractor} capable of extracting links in Hypermedia
* Application Language (HAL) format where the links are found in a map named
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java
index 528b444a..51b28f92 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java
@@ -19,6 +19,7 @@ package org.springframework.restdocs.hypermedia;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -50,22 +51,41 @@ public class LinksSnippet extends TemplatedSnippet {
private final LinkExtractor linkExtractor;
+ private final boolean ignoreUndocumentedLinks;
+
/**
* Creates a new {@code LinksSnippet} that will extract links using the given
* {@code linkExtractor} and document them using the given {@code descriptors}.
+ * Undocumented links will trigger a failure.
*
* @param linkExtractor the link extractor
* @param descriptors the link descriptors
*/
protected LinksSnippet(LinkExtractor linkExtractor,
List descriptors) {
- this(linkExtractor, descriptors, null);
+ this(linkExtractor, descriptors, null, false);
+ }
+
+ /**
+ * Creates a new {@code LinksSnippet} that will extract links using the given
+ * {@code linkExtractor} and document them using the given {@code descriptors}. If
+ * {@code ignoreUndocumentedLinks} is {@code true}, undocumented links will be ignored
+ * and will not trigger a failure.
+ *
+ * @param linkExtractor the link extractor
+ * @param descriptors the link descriptors
+ * @param ignoreUndocumentedLinks whether undocumented links should be ignored
+ */
+ protected LinksSnippet(LinkExtractor linkExtractor, List descriptors,
+ boolean ignoreUndocumentedLinks) {
+ this(linkExtractor, descriptors, null, ignoreUndocumentedLinks);
}
/**
* Creates a new {@code LinksSnippet} that will extract links using the given
* {@code linkExtractor} and document them using the given {@code descriptors}. The
* given {@code attributes} will be included in the model during template rendering.
+ * Undocumented links will trigger a failure.
*
* @param linkExtractor the link extractor
* @param descriptors the link descriptors
@@ -73,12 +93,30 @@ public class LinksSnippet extends TemplatedSnippet {
*/
protected LinksSnippet(LinkExtractor linkExtractor, List descriptors,
Map attributes) {
+ this(linkExtractor, descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code LinksSnippet} that will extract links using the given
+ * {@code linkExtractor} and document them using the given {@code descriptors}. The
+ * given {@code attributes} will be included in the model during template rendering.
+ * If {@code ignoreUndocumentedLinks} is {@code true}, undocumented links will be
+ * ignored and will not trigger a failure.
+ *
+ * @param linkExtractor the link extractor
+ * @param descriptors the link descriptors
+ * @param attributes the additional attributes
+ * @param ignoreUndocumentedLinks whether undocumented links should be ignored
+ */
+ protected LinksSnippet(LinkExtractor linkExtractor, List descriptors,
+ Map attributes, boolean ignoreUndocumentedLinks) {
super("links", attributes);
this.linkExtractor = linkExtractor;
for (LinkDescriptor descriptor : descriptors) {
Assert.notNull(descriptor.getRel(), "Link descriptors must have a rel");
this.descriptorsByRel.put(descriptor.getRel(), descriptor);
}
+ this.ignoreUndocumentedLinks = ignoreUndocumentedLinks;
}
@Override
@@ -100,8 +138,14 @@ public class LinksSnippet extends TemplatedSnippet {
private void validate(Map> links) {
Set actualRels = links.keySet();
- Set undocumentedRels = new HashSet<>(actualRels);
- undocumentedRels.removeAll(this.descriptorsByRel.keySet());
+ Set undocumentedRels;
+ if (this.ignoreUndocumentedLinks) {
+ undocumentedRels = Collections.emptySet();
+ }
+ else {
+ undocumentedRels = new HashSet<>(actualRels);
+ undocumentedRels.removeAll(this.descriptorsByRel.keySet());
+ }
Set requiredRels = new HashSet<>();
for (Entry relAndDescriptor : this.descriptorsByRel
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 19322b28..c6450712 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
@@ -39,20 +39,42 @@ import org.springframework.util.StringUtils;
*/
public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
- private List fieldDescriptors;
+ private final List fieldDescriptors;
+
+ private final boolean ignoreUndocumentedFields;
/**
* Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named
* {@code -fields}. The fields will be documented using the given
* {@code descriptors} and the given {@code attributes} will be included in the model
- * during template rendering.
+ * during template rendering. Undocumented fields will trigger a failure.
*
* @param type the type of the fields
* @param descriptors the field descriptors
* @param attributes the additional attributes
+ * @deprecated since 1.1 in favor of
+ * {@link #AbstractFieldsSnippet(String, List, Map, boolean)}
*/
+ @Deprecated
protected AbstractFieldsSnippet(String type, List descriptors,
Map attributes) {
+ this(type, descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named
+ * {@code -fields}. The fields will be documented using the given
+ * {@code descriptors} and the given {@code attributes} will be included in the model
+ * during template rendering. If {@code ignoreUndocumentedFields} is {@code true},
+ * undocumented fields will be ignored and will not trigger a failure.
+ *
+ * @param type the type of the fields
+ * @param descriptors the field descriptors
+ * @param attributes the additional attributes
+ * @param ignoreUndocumentedFields whether undocumented fields should be ignored
+ */
+ protected AbstractFieldsSnippet(String type, List descriptors,
+ Map attributes, boolean ignoreUndocumentedFields) {
super(type + "-fields", attributes);
for (FieldDescriptor descriptor : descriptors) {
Assert.notNull(descriptor.getPath(), "Field descriptors must have a path");
@@ -65,6 +87,7 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
}
this.fieldDescriptors = descriptors;
+ this.ignoreUndocumentedFields = ignoreUndocumentedFields;
}
@Override
@@ -111,8 +134,9 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
private void validateFieldDocumentation(ContentHandler payloadHandler) {
List missingFields = payloadHandler
.findMissingFields(this.fieldDescriptors);
- String undocumentedPayload = payloadHandler
- .getUndocumentedContent(this.fieldDescriptors);
+
+ String undocumentedPayload = this.ignoreUndocumentedFields ? null
+ : payloadHandler.getUndocumentedContent(this.fieldDescriptors);
if (!missingFields.isEmpty() || StringUtils.hasText(undocumentedPayload)) {
String message = "";
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java
index f2391277..bb94c960 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java
@@ -119,6 +119,22 @@ public abstract class PayloadDocumentation {
return new RequestFieldsSnippet(Arrays.asList(descriptors));
}
+ /**
+ * Returns a {@code Snippet} that will document the fields of the API operations's
+ * request payload. The fields will be documented using the given {@code descriptors}.
+ *
+ * If a field is documented, is not marked as optional, and is not present in the
+ * request, a failure will occur. Any undocumented fields will be ignored.
+ *
+ * @param descriptors the descriptions of the request payload's fields
+ * @return the snippet that will document the fields
+ * @see #fieldWithPath(String)
+ */
+ public static RequestFieldsSnippet relaxedRequestFields(
+ FieldDescriptor... descriptors) {
+ return new RequestFieldsSnippet(Arrays.asList(descriptors), true);
+ }
+
/**
* Returns a {@code Snippet} that will document the fields of the API operation's
* request payload. The fields will be documented using the given {@code descriptors}
@@ -145,6 +161,24 @@ public abstract class PayloadDocumentation {
return new RequestFieldsSnippet(Arrays.asList(descriptors), attributes);
}
+ /**
+ * Returns a {@code Snippet} that will document the fields of the API operation's
+ * request payload. The fields will be documented using the given {@code descriptors}
+ * and the given {@code attributes} will be available during snippet generation.
+ *
+ * If a field is documented, is not marked as optional, and is not present in the
+ * request, a failure will occur. Any undocumented fields will be ignored.
+ *
+ * @param attributes the attributes
+ * @param descriptors the descriptions of the request payload's fields
+ * @return the snippet that will document the fields
+ * @see #fieldWithPath(String)
+ */
+ public static RequestFieldsSnippet relaxedRequestFields(
+ Map attributes, FieldDescriptor... descriptors) {
+ return new RequestFieldsSnippet(Arrays.asList(descriptors), attributes, true);
+ }
+
/**
* Returns a {@code Snippet} that will document the fields of the API operation's
* response payload. The fields will be documented using the given {@code descriptors}
@@ -169,6 +203,23 @@ public abstract class PayloadDocumentation {
return new ResponseFieldsSnippet(Arrays.asList(descriptors));
}
+ /**
+ * Returns a {@code Snippet} that will document the fields of the API operation's
+ * response payload. The fields will be documented using the given {@code descriptors}
+ * .
+ *
+ * If a field is documented, is not marked as optional, and is not present in the
+ * request, a failure will occur. Any undocumented fields will be ignored.
+ *
+ * @param descriptors the descriptions of the response payload's fields
+ * @return the snippet that will document the fields
+ * @see #fieldWithPath(String)
+ */
+ public static ResponseFieldsSnippet relaxedResponseFields(
+ FieldDescriptor... descriptors) {
+ return new ResponseFieldsSnippet(Arrays.asList(descriptors), true);
+ }
+
/**
* Returns a {@code Snippet} that will document the fields of the API operation's
* response payload. The fields will be documented using the given {@code descriptors}
@@ -195,4 +246,22 @@ public abstract class PayloadDocumentation {
return new ResponseFieldsSnippet(Arrays.asList(descriptors), attributes);
}
+ /**
+ * Returns a {@code Snippet} that will document the fields of the API operation's
+ * response payload. The fields will be documented using the given {@code descriptors}
+ * and the given {@code attributes} will be available during snippet generation.
+ *
+ * If a field is documented, is not marked as optional, and is not present in the
+ * request, a failure will occur. Any undocumented fields will be ignored.
+ *
+ * @param attributes the attributes
+ * @param descriptors the descriptions of the response payload's fields
+ * @return the snippet that will document the fields
+ * @see #fieldWithPath(String)
+ */
+ public static ResponseFieldsSnippet relaxedResponseFields(
+ Map attributes, FieldDescriptor... descriptors) {
+ return new ResponseFieldsSnippet(Arrays.asList(descriptors), attributes, true);
+ }
+
}
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java
index 9e296887..cfb2ee26 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java
@@ -37,25 +37,56 @@ public class RequestFieldsSnippet extends AbstractFieldsSnippet {
/**
* Creates a new {@code RequestFieldsSnippet} that will document the fields in the
- * request using the given {@code descriptors}.
+ * request using the given {@code descriptors}. Undocumented fields will trigger a
+ * failure.
*
* @param descriptors the descriptors
*/
protected RequestFieldsSnippet(List descriptors) {
- this(descriptors, null);
+ this(descriptors, null, false);
+ }
+
+ /**
+ * Creates a new {@code RequestFieldsSnippet} that will document the fields in the
+ * request using the given {@code descriptors}. If {@code ignoreUndocumentedFields} is
+ * {@code true}, undocumented fields will be ignored and will not trigger a failure.
+ *
+ * @param descriptors the descriptors
+ * @param ignoreUndocumentedFields whether undocumented fields should be ignored
+ */
+ protected RequestFieldsSnippet(List descriptors,
+ boolean ignoreUndocumentedFields) {
+ this(descriptors, null, ignoreUndocumentedFields);
}
/**
* Creates a new {@code RequestFieldsSnippet} that will document the fields in the
* request using the given {@code descriptors}. The given {@code attributes} will be
- * included in the model during template rendering.
+ * included in the model during template rendering. Undocumented fields will trigger a
+ * failure.
*
* @param descriptors the descriptors
* @param attributes the additional attributes
*/
protected RequestFieldsSnippet(List descriptors,
Map attributes) {
- super("request", descriptors, attributes);
+ this(descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code RequestFieldsSnippet} that will document the fields in the
+ * request using the given {@code descriptors}. The given {@code attributes} will be
+ * included in the model during template rendering. If
+ * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be
+ * ignored and will not trigger a failure.
+ *
+ * @param descriptors the descriptors
+ * @param attributes the additional attributes
+ * @param ignoreUndocumentedFields whether undocumented fields should be ignored
+ */
+ protected RequestFieldsSnippet(List descriptors,
+ Map attributes, boolean ignoreUndocumentedFields) {
+ super("request", descriptors, attributes, ignoreUndocumentedFields);
}
@Override
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java
index 28815c4c..06b5ac65 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java
@@ -37,25 +37,57 @@ public class ResponseFieldsSnippet extends AbstractFieldsSnippet {
/**
* Creates a new {@code ResponseFieldsSnippet} that will document the fields in the
- * response using the given {@code descriptors}.
+ * response using the given {@code descriptors}. Undocumented fields will trigger a
+ * failure.
*
* @param descriptors the descriptors
*/
protected ResponseFieldsSnippet(List descriptors) {
- this(descriptors, null);
+ this(descriptors, null, false);
+ }
+
+ /**
+ * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the
+ * response using the given {@code descriptors}. If {@code ignoreUndocumentedFields}
+ * is {@code true}, undocumented fields will be ignored and will not trigger a
+ * failure.
+ *
+ * @param descriptors the descriptors
+ * @param ignoreUndocumentedFields whether undocumented fields should be ignored
+ */
+ protected ResponseFieldsSnippet(List descriptors,
+ boolean ignoreUndocumentedFields) {
+ this(descriptors, null, ignoreUndocumentedFields);
}
/**
* Creates a new {@code ResponseFieldsSnippet} that will document the fields in the
* response using the given {@code descriptors}. The given {@code attributes} will be
- * included in the model during template rendering.
+ * included in the model during template rendering. Undocumented fields will trigger a
+ * failure.
*
* @param descriptors the descriptors
* @param attributes the additional attributes
*/
protected ResponseFieldsSnippet(List descriptors,
Map attributes) {
- super("response", descriptors, attributes);
+ this(descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the
+ * response using the given {@code descriptors}. The given {@code attributes} will be
+ * included in the model during template rendering. If
+ * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be
+ * ignored and will not trigger a failure.
+ *
+ * @param descriptors the descriptors
+ * @param attributes the additional attributes
+ * @param ignoreUndocumentedFields whether undocumented fields should be ignored
+ */
+ protected ResponseFieldsSnippet(List descriptors,
+ Map attributes, boolean ignoreUndocumentedFields) {
+ super("response", descriptors, attributes, ignoreUndocumentedFields);
}
@Override
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java
index b132aa08..9d97737d 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java
@@ -17,6 +17,7 @@
package org.springframework.restdocs.request;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -40,18 +41,42 @@ public abstract class AbstractParametersSnippet extends TemplatedSnippet {
private final Map descriptorsByName = new LinkedHashMap<>();
+ private final boolean ignoreUndocumentedParameters;
+
/**
* Creates a new {@code AbstractParametersSnippet} that will produce a snippet with
* the given {@code snippetName} that will document parameters using the given
* {@code descriptors}. The given {@code attributes} will be included in the model
- * during template rendering.
+ * during template rendering. Undocumented parameters will trigger a failure.
*
* @param snippetName The snippet name
* @param descriptors The descriptors
* @param attributes The additional attributes
+ * @deprecated since 1.1 in favour of
+ * {@link #AbstractParametersSnippet(String, List, Map, boolean)}
*/
+ @Deprecated
protected AbstractParametersSnippet(String snippetName,
List descriptors, Map attributes) {
+ this(snippetName, descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code AbstractParametersSnippet} that will produce a snippet with
+ * the given {@code snippetName} that will document parameters using the given
+ * {@code descriptors}. The given {@code attributes} will be included in the model
+ * during template rendering. If {@code ignoreUndocumentedParameters} is {@code true},
+ * undocumented parameters will be ignored and will not trigger a failure.
+ *
+ * @param snippetName The snippet name
+ * @param descriptors The descriptors
+ * @param attributes The additional attributes
+ * @param ignoreUndocumentedParameters whether undocumented parameters should be
+ * ignored
+ */
+ protected AbstractParametersSnippet(String snippetName,
+ List descriptors, Map attributes,
+ boolean ignoreUndocumentedParameters) {
super(snippetName, attributes);
for (ParameterDescriptor descriptor : descriptors) {
Assert.notNull(descriptor.getName(),
@@ -64,6 +89,7 @@ public abstract class AbstractParametersSnippet extends TemplatedSnippet {
}
this.descriptorsByName.put(descriptor.getName(), descriptor);
}
+ this.ignoreUndocumentedParameters = ignoreUndocumentedParameters;
}
@Override
@@ -92,8 +118,14 @@ public abstract class AbstractParametersSnippet extends TemplatedSnippet {
expectedParameters.add(entry.getKey());
}
}
- Set undocumentedParameters = new HashSet<>(actualParameters);
- undocumentedParameters.removeAll(expectedParameters);
+ Set undocumentedParameters;
+ if (this.ignoreUndocumentedParameters) {
+ undocumentedParameters = Collections.emptySet();
+ }
+ else {
+ undocumentedParameters = new HashSet<>(actualParameters);
+ undocumentedParameters.removeAll(expectedParameters);
+ }
Set missingParameters = new HashSet<>(expectedParameters);
missingParameters.removeAll(actualParameters);
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java
index 01ef25e4..509a604b 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java
@@ -44,25 +44,59 @@ public class PathParametersSnippet extends AbstractParametersSnippet {
/**
* Creates a new {@code PathParametersSnippet} that will document the request's path
- * parameters using the given {@code descriptors}.
+ * parameters using the given {@code descriptors}. Undocumented parameters will
+ * trigger a failure.
*
* @param descriptors the parameter descriptors
*/
protected PathParametersSnippet(List descriptors) {
- this(descriptors, null);
+ this(descriptors, null, false);
+ }
+
+ /**
+ * Creates a new {@code PathParametersSnippet} that will document the request's path
+ * parameters using the given {@code descriptors}. If
+ * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
+ * be ignored and will not trigger a failure.
+ *
+ * @param descriptors the parameter descriptors
+ * @param ignoreUndocumentedParameters whether undocumented parameters should be
+ * ignored
+ */
+ protected PathParametersSnippet(List descriptors,
+ boolean ignoreUndocumentedParameters) {
+ this(descriptors, null, ignoreUndocumentedParameters);
}
/**
* Creates a new {@code PathParametersSnippet} that will document the request's path
* parameters using the given {@code descriptors}. The given {@code attributes} will
- * be included in the model during template rendering.
+ * be included in the model during template rendering. Undocumented parameters will
+ * trigger a failure.
*
* @param descriptors the parameter descriptors
* @param attributes the additional attributes
*/
protected PathParametersSnippet(List descriptors,
Map attributes) {
- super("path-parameters", descriptors, attributes);
+ this(descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code PathParametersSnippet} that will document the request's path
+ * parameters using the given {@code descriptors}. The given {@code attributes} will
+ * be included in the model during template rendering. If
+ * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
+ * be ignored and will not trigger a failure.
+ *
+ * @param descriptors the parameter descriptors
+ * @param attributes the additional attributes
+ * @param ignoreUndocumentedParameters whether undocumented parameters should be
+ * ignored
+ */
+ protected PathParametersSnippet(List descriptors,
+ Map attributes, boolean ignoreUndocumentedParameters) {
+ super("path-parameters", descriptors, attributes, ignoreUndocumentedParameters);
}
@Override
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java
index 6fa64c9b..4c4aee73 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 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.
@@ -65,6 +65,22 @@ public abstract class RequestDocumentation {
return new PathParametersSnippet(Arrays.asList(descriptors));
}
+ /**
+ * Returns a {@code Snippet} that will document the path parameters from the API
+ * operation's request. The parameters will be documented using the given
+ * {@code descriptors}.
+ *
+ * If a parameter is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented parameters will be ignored.
+ *
+ * @param descriptors the descriptions of the parameters in the request's path
+ * @return the snippet that will document the parameters
+ */
+ public static PathParametersSnippet relaxedPathParameters(
+ ParameterDescriptor... descriptors) {
+ return new PathParametersSnippet(Arrays.asList(descriptors), true);
+ }
+
/**
* Returns a {@code Snippet} that will document the path parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
@@ -89,6 +105,24 @@ public abstract class RequestDocumentation {
return new PathParametersSnippet(Arrays.asList(descriptors), attributes);
}
+ /**
+ * Returns a {@code Snippet} that will document the path parameters from the API
+ * operation's request. The given {@code attributes} will be available during snippet
+ * rendering and the parameters will be documented using the given {@code descriptors}
+ * .
+ *
+ * If a parameter is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented parameters will be ignored.
+ *
+ * @param attributes the attributes
+ * @param descriptors the descriptions of the parameters in the request's path
+ * @return the snippet that will document the parameters
+ */
+ public static PathParametersSnippet relaxedPathParameters(
+ Map attributes, ParameterDescriptor... descriptors) {
+ return new PathParametersSnippet(Arrays.asList(descriptors), attributes, true);
+ }
+
/**
* Returns a {@code Snippet} that will document the parameters from the API
* operation's request. The parameters will be documented using the given
@@ -112,6 +146,23 @@ public abstract class RequestDocumentation {
return new RequestParametersSnippet(Arrays.asList(descriptors));
}
+ /**
+ * Returns a {@code Snippet} that will document the parameters from the API
+ * operation's request. The parameters will be documented using the given
+ * {@code descriptors}.
+ *
+ * If a parameter is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented parameters will be ignored.
+ *
+ * @param descriptors The descriptions of the request's parameters
+ * @return the snippet
+ * @see OperationRequest#getParameters()
+ */
+ public static RequestParametersSnippet relaxedRequestParameters(
+ ParameterDescriptor... descriptors) {
+ return new RequestParametersSnippet(Arrays.asList(descriptors), true);
+ }
+
/**
* Returns a {@code Snippet} that will document the parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
@@ -137,4 +188,23 @@ public abstract class RequestDocumentation {
return new RequestParametersSnippet(Arrays.asList(descriptors), attributes);
}
+ /**
+ * Returns a {@code Snippet} that will document the parameters from the API
+ * operation's request. The given {@code attributes} will be available during snippet
+ * rendering and the parameters will be documented using the given {@code descriptors}
+ * .
+ *
+ * If a parameter is documented, is not marked as optional, and is not present in the
+ * response, a failure will occur. Any undocumented parameters will be ignored.
+ *
+ * @param attributes the attributes
+ * @param descriptors the descriptions of the request's parameters
+ * @return the snippet that will document the parameters
+ * @see OperationRequest#getParameters()
+ */
+ public static RequestParametersSnippet relaxedRequestParameters(
+ Map attributes, ParameterDescriptor... descriptors) {
+ return new RequestParametersSnippet(Arrays.asList(descriptors), attributes, true);
+ }
+
}
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java
index e85423fc..37a5696d 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 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.
@@ -42,25 +42,60 @@ public class RequestParametersSnippet extends AbstractParametersSnippet {
/**
* Creates a new {@code RequestParametersSnippet} that will document the request's
- * parameters using the given {@code descriptors}.
+ * parameters using the given {@code descriptors}. Undocumented parameters will
+ * trigger a failure.
*
* @param descriptors the parameter descriptors
*/
protected RequestParametersSnippet(List descriptors) {
- this(descriptors, null);
+ this(descriptors, null, false);
+ }
+
+ /**
+ * Creates a new {@code RequestParametersSnippet} that will document the request's
+ * parameters using the given {@code descriptors}. If
+ * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
+ * be ignored and will not trigger a failure.
+ *
+ * @param descriptors the parameter descriptors
+ * @param ignoreUndocumentedParameters whether undocumented parameters should be
+ * ignored
+ */
+ protected RequestParametersSnippet(List descriptors,
+ boolean ignoreUndocumentedParameters) {
+ this(descriptors, null, ignoreUndocumentedParameters);
}
/**
* Creates a new {@code RequestParametersSnippet} that will document the request's
* parameters using the given {@code descriptors}. The given {@code attributes} will
- * be included in the model during template rendering.
+ * be included in the model during template rendering. Undocumented parameters will
+ * trigger a failure.
*
* @param descriptors the parameter descriptors
* @param attributes the additional attributes
*/
protected RequestParametersSnippet(List descriptors,
Map attributes) {
- super("request-parameters", descriptors, attributes);
+ this(descriptors, attributes, false);
+ }
+
+ /**
+ * Creates a new {@code RequestParametersSnippet} that will document the request's
+ * parameters using the given {@code descriptors}. The given {@code attributes} will
+ * be included in the model during template rendering. If
+ * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
+ * be ignored and will not trigger a failure.
+ *
+ * @param descriptors the parameter descriptors
+ * @param attributes the additional attributes
+ * @param ignoreUndocumentedParameters whether undocumented parameters should be
+ * ignored
+ */
+ protected RequestParametersSnippet(List descriptors,
+ Map attributes, boolean ignoreUndocumentedParameters) {
+ super("request-parameters", descriptors, attributes,
+ ignoreUndocumentedParameters);
}
@Override
diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java
index be122de6..e94abfc3 100644
--- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java
+++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java
@@ -56,6 +56,17 @@ public class LinksSnippetTests extends AbstractSnippetTests {
.document(operationBuilder("ignored-link").build());
}
+ @Test
+ public void allUndocumentedLinksCanBeIgnored() throws IOException {
+ this.snippet.expectLinks("ignore-all-undocumented").withContents(
+ tableWithHeader("Relation", "Description").row("b", "Link b"));
+ new LinksSnippet(
+ new StubLinkExtractor().withLinks(new Link("a", "alpha"),
+ new Link("b", "bravo")),
+ Arrays.asList(new LinkDescriptor("b").description("Link b")), true)
+ .document(operationBuilder("ignore-all-undocumented").build());
+ }
+
@Test
public void documentedOptionalLink() throws IOException {
this.snippet.expectLinks("documented-optional-link").withContents(
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 79e02a8c..d94a9071 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
@@ -93,6 +93,19 @@ public class RequestFieldsSnippetTests extends AbstractSnippetTests {
.content("{\"a\": 5, \"b\": 4}").build());
}
+ @Test
+ public void allUndocumentedRequestFieldsCanBeIgnored() throws IOException {
+ this.snippet.expectRequestFields("ignore-all-undocumented")
+ .withContents(tableWithHeader("Path", "Type", "Description").row("b",
+ "Number", "Field b"));
+
+ new RequestFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")),
+ true).document(
+ operationBuilder("ignore-all-undocumented")
+ .request("http://localhost")
+ .content("{\"a\": 5, \"b\": 4}").build());
+ }
+
@Test
public void requestFieldsWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
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 ae490e50..215eb4c8 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
@@ -105,6 +105,18 @@ public class ResponseFieldsSnippetTests extends AbstractSnippetTests {
.content("{\"a\": 5, \"b\": 4}").build());
}
+ @Test
+ public void allUndocumentedFieldsCanBeIgnored() throws IOException {
+ this.snippet.expectResponseFields("ignore-all-undocumented")
+ .withContents(tableWithHeader("Path", "Type", "Description").row("b",
+ "Number", "Field b"));
+
+ new ResponseFieldsSnippet(
+ Arrays.asList(fieldWithPath("b").description("Field b")), true)
+ .document(operationBuilder("ignore-all-undocumented").response()
+ .content("{\"a\": 5, \"b\": 4}").build());
+ }
+
@Test
public void responseFieldsWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java
index 05ca00c9..c3443048 100644
--- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java
+++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java
@@ -71,6 +71,19 @@ public class PathParametersSnippetTests extends AbstractSnippetTests {
"/{a}/{b}").build());
}
+ @Test
+ public void allUndocumentedPathParametersCanBeIgnored() throws IOException {
+ this.snippet.expectPathParameters("ignore-all-undocumented").withContents(
+ tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("b",
+ "two"));
+ new PathParametersSnippet(
+ Arrays.asList(parameterWithName("b").description("two")),
+ true).document(operationBuilder("ignore-all-undocumented")
+ .attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE,
+ "/{a}/{b}")
+ .build());
+ }
+
@Test
public void missingOptionalPathParameter() throws IOException {
this.snippet
diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java
index 16baa187..a62c5de2 100644
--- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java
+++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java
@@ -80,6 +80,17 @@ public class RequestParametersSnippetTests extends AbstractSnippetTests {
.param("b", "bravo").build());
}
+ @Test
+ public void allUndocumentedRequestParametersCanBeIgnored() throws IOException {
+ this.snippet.expectRequestParameters("ignore-all-undocumented").withContents(
+ tableWithHeader("Parameter", "Description").row("b", "two"));
+ new RequestParametersSnippet(
+ Arrays.asList(parameterWithName("b").description("two")), true)
+ .document(operationBuilder("ignore-all-undocumented")
+ .request("http://localhost").param("a", "bravo")
+ .param("b", "bravo").build());
+ }
+
@Test
public void missingOptionalRequestParameter() throws IOException {
this.snippet.expectRequestParameters("missing-optional-request-parameter")