Polish "Add support for documenting request part payload fields"
- Rebase on latest code, and make use of new support for the same TemplatedSnippet producing multiple snippets with different names from the same template - Expand the documentation - Apply code formatting - Add support for relaxed documentation of a request part's fields Closes gh-270
This commit is contained in:
@@ -415,7 +415,7 @@ include::{examples-dir}/com/example/mockmvc/RequestParameters.java[tags=request-
|
||||
----
|
||||
<1> Perform a `GET` request with two parameters, `page` and `per_page` in the query
|
||||
string.
|
||||
<2> Configure Spring REST Docs to produce a snippet describing the request's parameters.
|
||||
<2> Configure Spring REST Docs to produce a snippet describing the request's parameters.
|
||||
Uses the static `requestParameters` method on
|
||||
`org.springframework.restdocs.request.RequestDocumentation`.
|
||||
<3> Document the `page` parameter. Uses the static `parameterWithName` method on
|
||||
@@ -575,12 +575,58 @@ prevent it from appearing in the generated snippet while avoiding the failure de
|
||||
above.
|
||||
|
||||
|
||||
[[documenting-your-api-request-parts]]
|
||||
=== Request parts content
|
||||
[[documenting-your-api-request-parts-payloads]]
|
||||
=== Request part payloads
|
||||
|
||||
If you need to document the content of request part that holds Json data, you should use`requestPartsFields`.
|
||||
It acts exactly as [[documenting-your-api-request-parts]] but in order to use it, you must specify the part name.
|
||||
The payload of a request part can be documented in much the same way as the
|
||||
<<documenting-your-api-request-response-payloads,payload of a request>>:
|
||||
|
||||
[source,java,indent=0,role="primary"]
|
||||
.MockMvc
|
||||
----
|
||||
include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=payload]
|
||||
----
|
||||
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
|
||||
of the request part named `metadata`. Uses the static `requestPartFields` method on
|
||||
`PayloadDocumentation`.
|
||||
payload.
|
||||
<2> Expect a field with the path `version`. Uses the static `fieldWithPath` method on
|
||||
`org.springframework.restdocs.payload.PayloadDocumentation`.
|
||||
|
||||
[source,java,indent=0,role="secondary"]
|
||||
.REST Assured
|
||||
----
|
||||
include::{examples-dir}/com/example/restassured/RequestPartPayload.java[tags=payload]
|
||||
----
|
||||
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
|
||||
of the request part named `metadata`. Uses the static `requestPartFields` method on
|
||||
`PayloadDocumentation`.
|
||||
<2> Expect a field with the path `version`. Uses the static `fieldWithPath` method on
|
||||
`org.springframework.restdocs.payload.PayloadDocumentation`.
|
||||
|
||||
The result is a snippet that contains a table describing the part's fields. This snippet
|
||||
is named `request-part-${part-name}-fields.adoc`. For example, documenting a part named
|
||||
`metadata` will produce a snippet named `request-part-metadata-fields.adoc`.
|
||||
|
||||
When documenting fields, the test will fail if an undocumented field is found in the
|
||||
payload of the part. Similarly, the test will also fail if a documented field is not found
|
||||
in the payload of the part and the field has not been marked as optional. For payloads
|
||||
with a hierarchical structure, documenting a field is sufficient for all of its
|
||||
descendants to also be treated as having been documented.
|
||||
|
||||
If you do not want to document a field, you can mark it as ignored. This will prevent it
|
||||
from appearing in the generated snippet while avoiding the failure described above.
|
||||
|
||||
Fields can also be documented in a relaxed mode where any undocumented fields will not
|
||||
cause a test failure. To do so, use the `relaxedRequestPartFields` method on
|
||||
`org.springframework.restdocs.payload.PayloadDocumentation`. This can be useful when
|
||||
documenting a particular scenario where you only want to focus on a subset of the payload
|
||||
of the part.
|
||||
|
||||
For further information on describing fields, documenting payloads that use XML,
|
||||
and more please refer to the
|
||||
<<documenting-your-api-request-response-payloads,section on documenting request and
|
||||
response payloads>>.
|
||||
|
||||
[[documenting-your-api-http-headers]]
|
||||
=== HTTP headers
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.mockmvc;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.fileUpload;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
public class RequestPartPayload {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
public void response() throws Exception {
|
||||
// tag::payload[]
|
||||
MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png",
|
||||
"<<png data>>".getBytes());
|
||||
MockMultipartFile metadata = new MockMultipartFile("metadata", "",
|
||||
"application/json", "{ \"version\": \"1.0\"}".getBytes());
|
||||
|
||||
this.mockMvc.perform(fileUpload("/images").file(image).file(metadata)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("image-upload", requestPartFields("metadata", // <1>
|
||||
fieldWithPath("version").description("The version of the image")))); // <2>
|
||||
// end::payload[]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.restassured;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jayway.restassured.RestAssured;
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
|
||||
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
|
||||
|
||||
public class RequestPartPayload {
|
||||
|
||||
private RequestSpecification spec;
|
||||
|
||||
public void response() throws Exception {
|
||||
// tag::payload[]
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put("version", "1.0");
|
||||
RestAssured.given(this.spec).accept("application/json")
|
||||
.filter(document("image-upload", requestPartFields("metadata", // <1>
|
||||
fieldWithPath("version").description("The version of the image")))) // <2>
|
||||
.when().multiPart("image", new File("image.png"), "image/png")
|
||||
.multiPart("metadata", metadata).post("images")
|
||||
.then().assertThat().statusCode(is(200));
|
||||
// end::payload[]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,10 +65,11 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
|
||||
|
||||
/**
|
||||
* Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named
|
||||
* {@code <type>-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.
|
||||
* {@code <type>-fields} using a template named {@code <type>-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
|
||||
@@ -77,7 +78,27 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
|
||||
*/
|
||||
protected AbstractFieldsSnippet(String type, List<FieldDescriptor> descriptors,
|
||||
Map<String, Object> attributes, boolean ignoreUndocumentedFields) {
|
||||
super(type + "-fields", attributes);
|
||||
this(type, type, descriptors, attributes, ignoreUndocumentedFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named
|
||||
* {@code <name>-fields} using a template named {@code <type>-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 name the name of the snippet
|
||||
* @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 name, String type,
|
||||
List<FieldDescriptor> descriptors, Map<String, Object> attributes,
|
||||
boolean ignoreUndocumentedFields) {
|
||||
super(name + "-fields", type + "-fields", attributes);
|
||||
for (FieldDescriptor descriptor : descriptors) {
|
||||
Assert.notNull(descriptor.getPath(), "Field descriptors must have a path");
|
||||
if (!descriptor.isIgnored()) {
|
||||
|
||||
@@ -148,56 +148,6 @@ public abstract class PayloadDocumentation {
|
||||
return new RequestFieldsSnippet(descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified {@code part}
|
||||
* of the API operations's request payload. The fields will be documented using the
|
||||
* given {@code descriptors}.
|
||||
* <p>
|
||||
* If a field is present in the request payload, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request,
|
||||
* a failure will also occur. For payloads with a hierarchical structure, documenting
|
||||
* a field is sufficient for all of its descendants to also be treated as having been
|
||||
* documented.
|
||||
* <p>
|
||||
* If you do not want to document a field, a field descriptor can be marked as
|
||||
* {@link FieldDescriptor#ignored}. This will prevent it from appearing in the
|
||||
* generated snippet while avoiding the failure described above.
|
||||
*
|
||||
* @param part the part name
|
||||
* @param descriptors the descriptions of the request payload's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet requestPartFields(String part, FieldDescriptor... descriptors) {
|
||||
return requestPartFields(part, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified {@code part}
|
||||
* of the API operations's request payload. The fields will be documented using the given
|
||||
* {@code descriptors}.
|
||||
* <p>
|
||||
* If a field is present in the request payload, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request,
|
||||
* a failure will also occur. For payloads with a hierarchical structure, documenting
|
||||
* a field is sufficient for all of its descendants to also be treated as having been
|
||||
* documented.
|
||||
* <p>
|
||||
* If you do not want to document a field, a field descriptor can be marked as
|
||||
* {@link FieldDescriptor#ignored}. This will prevent it from appearing in the
|
||||
* generated snippet while avoiding the failure described above.
|
||||
*
|
||||
* @param part the part name
|
||||
* @param descriptors the descriptions of the request payload's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet requestPartFields(String part, List<FieldDescriptor> descriptors) {
|
||||
return new RequestPartFieldsSnippet(part, 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}.
|
||||
@@ -318,6 +268,190 @@ public abstract class PayloadDocumentation {
|
||||
return new RequestFieldsSnippet(descriptors, attributes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a field is present in the request part, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request
|
||||
* part, a failure will also occur. For payloads with a hierarchical structure,
|
||||
* documenting a field is sufficient for all of its descendants to also be treated as
|
||||
* having been documented.
|
||||
* <p>
|
||||
* If you do not want to document a field, a field descriptor can be marked as
|
||||
* {@link FieldDescriptor#ignored}. This will prevent it from appearing in the
|
||||
* generated snippet while avoiding the failure described above.
|
||||
*
|
||||
* @param part the part name
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet requestPartFields(String part,
|
||||
FieldDescriptor... descriptors) {
|
||||
return requestPartFields(part, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a field is present in the request part, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request
|
||||
* part, a failure will also occur. For payloads with a hierarchical structure,
|
||||
* documenting a field is sufficient for all of its descendants to also be treated as
|
||||
* having been documented.
|
||||
* <p>
|
||||
* If you do not want to document a field, a field descriptor can be marked as
|
||||
* {@link FieldDescriptor#ignored}. This will prevent it from appearing in the
|
||||
* generated snippet while avoiding the failure described above.
|
||||
*
|
||||
* @param part the part name
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet requestPartFields(String part,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
return new RequestPartFieldsSnippet(part, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors}.
|
||||
* <p>
|
||||
* 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 part the part name
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet relaxedRequestPartFields(String part,
|
||||
FieldDescriptor... descriptors) {
|
||||
return relaxedRequestPartFields(part, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors}.
|
||||
* <p>
|
||||
* 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 part the part name
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet relaxedRequestPartFields(String part,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
return new RequestPartFieldsSnippet(part, descriptors, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors} and the given {@code attributes} will be
|
||||
* available during snippet generation.
|
||||
* <p>
|
||||
* If a field is present in the request part, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request
|
||||
* part, a failure will also occur. For payloads with a hierarchical structure,
|
||||
* documenting a field is sufficient for all of its descendants to also be treated as
|
||||
* having been documented.
|
||||
* <p>
|
||||
* If you do not want to document a field, a field descriptor can be marked as
|
||||
* {@link FieldDescriptor#ignored}. This will prevent it from appearing in the
|
||||
* generated snippet while avoiding the failure described above.
|
||||
*
|
||||
* @param part the part name
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet requestPartFields(String part,
|
||||
Map<String, Object> attributes, FieldDescriptor... descriptors) {
|
||||
return requestPartFields(part, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors} and the given {@code attributes} will be
|
||||
* available during snippet generation.
|
||||
* <p>
|
||||
* If a field is present in the request part, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request
|
||||
* part, a failure will also occur. For payloads with a hierarchical structure,
|
||||
* documenting a field is sufficient for all of its descendants to also be treated as
|
||||
* having been documented.
|
||||
* <p>
|
||||
* If you do not want to document a field, a field descriptor can be marked as
|
||||
* {@link FieldDescriptor#ignored}. This will prevent it from appearing in the
|
||||
* generated snippet while avoiding the failure described above.
|
||||
*
|
||||
* @param part the part name
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet requestPartFields(String part,
|
||||
Map<String, Object> attributes, List<FieldDescriptor> descriptors) {
|
||||
return new RequestPartFieldsSnippet(part, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors} and the given {@code attributes} will be
|
||||
* available during snippet generation.
|
||||
* <p>
|
||||
* 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 part the part name
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet relaxedRequestPartFields(String part,
|
||||
Map<String, Object> attributes, FieldDescriptor... descriptors) {
|
||||
return relaxedRequestPartFields(part, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Snippet} that will document the fields of the specified
|
||||
* {@code part} of the API operations's request payload. The fields will be documented
|
||||
* using the given {@code descriptors} and the given {@code attributes} will be
|
||||
* available during snippet generation.
|
||||
* <p>
|
||||
* 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 part the part name
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request part's fields
|
||||
* @return the snippet that will document the fields
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static RequestPartFieldsSnippet relaxedRequestPartFields(String part,
|
||||
Map<String, Object> attributes, List<FieldDescriptor> descriptors) {
|
||||
return new RequestPartFieldsSnippet(part, 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}
|
||||
|
||||
@@ -29,10 +29,12 @@ import org.springframework.restdocs.snippet.Snippet;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
|
||||
/**
|
||||
* A {@link Snippet} that documents the fields in a request.
|
||||
* A {@link Snippet} that documents the fields in a request part.
|
||||
*
|
||||
* @author Mathieu Pousse
|
||||
* @author Andy Wilkinson
|
||||
* @see PayloadDocumentation#requestPartFields(String, FieldDescriptor...)
|
||||
* @see PayloadDocumentation#requestPartFields(String, List)
|
||||
*/
|
||||
public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
|
||||
@@ -40,27 +42,29 @@ public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestPartFieldsSnippet} that will document the fields in the
|
||||
* request part using the given {@code descriptors}. Undocumented fields will trigger a
|
||||
* failure.
|
||||
* request part using the given {@code descriptors}. Undocumented fields will trigger
|
||||
* a failure.
|
||||
*
|
||||
* @param partName the part name
|
||||
* @param partName the part name
|
||||
* @param descriptors the descriptors
|
||||
*/
|
||||
protected RequestPartFieldsSnippet(String partName, List<FieldDescriptor> descriptors) {
|
||||
protected RequestPartFieldsSnippet(String partName,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
this(partName, descriptors, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestPartFieldsSnippet} that will document the fields in the
|
||||
* request part using the given {@code descriptors}. If {@code ignoreUndocumentedFields} is
|
||||
* {@code true}, undocumented fields will be ignored and will not trigger a failure.
|
||||
* request part using the given {@code descriptors}. If
|
||||
* {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be
|
||||
* ignored and will not trigger a failure.
|
||||
*
|
||||
* @param partName the part name
|
||||
* @param descriptors the descriptors
|
||||
* @param partName the part name
|
||||
* @param descriptors the descriptors
|
||||
* @param ignoreUndocumentedFields whether undocumented fields should be ignored
|
||||
*/
|
||||
protected RequestPartFieldsSnippet(String partName, List<FieldDescriptor> descriptors,
|
||||
boolean ignoreUndocumentedFields) {
|
||||
boolean ignoreUndocumentedFields) {
|
||||
this(partName, descriptors, null, ignoreUndocumentedFields);
|
||||
}
|
||||
|
||||
@@ -70,12 +74,12 @@ public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
* included in the model during template rendering. Undocumented fields will trigger a
|
||||
* failure.
|
||||
*
|
||||
* @param partName the part name
|
||||
* @param partName the part name
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
* @param attributes the additional attributes
|
||||
*/
|
||||
protected RequestPartFieldsSnippet(String partName, List<FieldDescriptor> descriptors,
|
||||
Map<String, Object> attributes) {
|
||||
Map<String, Object> attributes) {
|
||||
this(partName, descriptors, attributes, false);
|
||||
}
|
||||
|
||||
@@ -86,44 +90,36 @@ public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
* {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be
|
||||
* ignored and will not trigger a failure.
|
||||
*
|
||||
* @param partName the part name
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
* @param partName the part name
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
* @param ignoreUndocumentedFields whether undocumented fields should be ignored
|
||||
*/
|
||||
protected RequestPartFieldsSnippet(String partName, List<FieldDescriptor> descriptors,
|
||||
Map<String, Object> attributes, boolean ignoreUndocumentedFields) {
|
||||
super("request", descriptors, attributes, ignoreUndocumentedFields);
|
||||
Map<String, Object> attributes, boolean ignoreUndocumentedFields) {
|
||||
super("request-part-" + partName, "request-part", descriptors, attributes,
|
||||
ignoreUndocumentedFields);
|
||||
this.partName = partName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getContentType(Operation operation) {
|
||||
for (OperationRequestPart candidate : operation.getRequest().getParts()) {
|
||||
if (candidate.getName().equals(this.partName)) {
|
||||
return candidate.getHeaders().getContentType();
|
||||
}
|
||||
}
|
||||
throw new SnippetException(missingPartErrorMessage());
|
||||
return findPart(operation).getHeaders().getContentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getContent(Operation operation) throws IOException {
|
||||
for (OperationRequestPart candidate : operation.getRequest().getParts()) {
|
||||
if (candidate.getName().equals(this.partName)) {
|
||||
return candidate.getContent();
|
||||
}
|
||||
}
|
||||
throw new SnippetException(missingPartErrorMessage());
|
||||
return findPart(operation).getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the error message because the requested part was not found.
|
||||
*
|
||||
* @return see description
|
||||
*/
|
||||
protected String missingPartErrorMessage() {
|
||||
return "Request parts with the following names were not found in the request: " + this.partName;
|
||||
private OperationRequestPart findPart(Operation operation) {
|
||||
for (OperationRequestPart candidate : operation.getRequest().getParts()) {
|
||||
if (candidate.getName().equals(this.partName)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new SnippetException("A request part named '" + this.partName
|
||||
+ "' was not found in the request");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +142,8 @@ public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final RequestPartFieldsSnippet and(List<FieldDescriptor> additionalDescriptors) {
|
||||
public final RequestPartFieldsSnippet and(
|
||||
List<FieldDescriptor> additionalDescriptors) {
|
||||
return andWithPrefix("", additionalDescriptors);
|
||||
}
|
||||
|
||||
@@ -156,17 +153,18 @@ public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
* {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path
|
||||
* of each additional descriptor.
|
||||
*
|
||||
* @param pathPrefix the prefix to apply to the additional descriptors
|
||||
* @param pathPrefix the prefix to apply to the additional descriptors
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final RequestPartFieldsSnippet andWithPrefix(String pathPrefix,
|
||||
FieldDescriptor... additionalDescriptors) {
|
||||
FieldDescriptor... additionalDescriptors) {
|
||||
List<FieldDescriptor> combinedDescriptors = new ArrayList<>();
|
||||
combinedDescriptors.addAll(getFieldDescriptors());
|
||||
combinedDescriptors.addAll(
|
||||
PayloadDocumentation.applyPathPrefix(pathPrefix, Arrays.asList(additionalDescriptors)));
|
||||
return new RequestPartFieldsSnippet(this.partName, combinedDescriptors, this.getAttributes());
|
||||
combinedDescriptors.addAll(PayloadDocumentation.applyPathPrefix(pathPrefix,
|
||||
Arrays.asList(additionalDescriptors)));
|
||||
return new RequestPartFieldsSnippet(this.partName, combinedDescriptors,
|
||||
this.getAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,17 +173,18 @@ public class RequestPartFieldsSnippet extends AbstractFieldsSnippet {
|
||||
* {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path
|
||||
* of each additional descriptor.
|
||||
*
|
||||
* @param pathPrefix the prefix to apply to the additional descriptors
|
||||
* @param pathPrefix the prefix to apply to the additional descriptors
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final RequestPartFieldsSnippet andWithPrefix(String pathPrefix,
|
||||
List<FieldDescriptor> additionalDescriptors) {
|
||||
List<FieldDescriptor> additionalDescriptors) {
|
||||
List<FieldDescriptor> combinedDescriptors = new ArrayList<>(
|
||||
getFieldDescriptors());
|
||||
combinedDescriptors.addAll(
|
||||
PayloadDocumentation.applyPathPrefix(pathPrefix, additionalDescriptors));
|
||||
return new RequestPartFieldsSnippet(this.partName, combinedDescriptors, this.getAttributes());
|
||||
return new RequestPartFieldsSnippet(this.partName, combinedDescriptors,
|
||||
this.getAttributes());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Path|Type|Description
|
||||
|
||||
{{#fields}}
|
||||
|{{#tableCellContent}}`{{path}}`{{/tableCellContent}}
|
||||
|{{#tableCellContent}}`{{type}}`{{/tableCellContent}}
|
||||
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
@@ -0,0 +1,5 @@
|
||||
Path | Type | Description
|
||||
---- | ---- | -----------
|
||||
{{#fields}}
|
||||
`{{path}}` | `{{type}}` | {{description}}
|
||||
{{/fields}}
|
||||
@@ -26,7 +26,6 @@ import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
@@ -38,11 +37,13 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWit
|
||||
* undocumented fields.
|
||||
*
|
||||
* @author Mathieu Pousse
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestPartsFieldsSnippetFailureTests {
|
||||
public class RequestPartFieldsSnippetFailureTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedSnippet snippet = new ExpectedSnippet(TemplateFormats.asciidoctor());
|
||||
public OperationBuilder operationBuilder = new OperationBuilder(
|
||||
TemplateFormats.asciidoctor());
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
@@ -50,33 +51,33 @@ public class RequestPartsFieldsSnippetFailureTests {
|
||||
@Test
|
||||
public void undocumentedRequestPartField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(startsWith(
|
||||
"The following parts of the payload were not" + " documented:"));
|
||||
this.thrown.expectMessage(
|
||||
startsWith("The following parts of the payload were not documented:"));
|
||||
new RequestPartFieldsSnippet("part", Collections.<FieldDescriptor>emptyList())
|
||||
.document(new OperationBuilder("undocumented-request-field",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.part("part", "{\"a\": 5}".getBytes()).build());
|
||||
.document(this.operationBuilder.request("http://localhost")
|
||||
.part("part", "{\"a\": 5}".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingRequestPartField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(startsWith(
|
||||
"The following parts of the payload were not" + " documented:"));
|
||||
new RequestPartFieldsSnippet("part", Arrays.asList(fieldWithPath("b").description("one")))
|
||||
.document(new OperationBuilder("undocumented-request-field",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.part("part", "{\"a\": 5}".getBytes()).build());
|
||||
this.thrown.expectMessage(
|
||||
startsWith("The following parts of the payload were not documented:"));
|
||||
new RequestPartFieldsSnippet("part",
|
||||
Arrays.asList(fieldWithPath("b").description("one")))
|
||||
.document(this.operationBuilder.request("http://localhost")
|
||||
.part("part", "{\"a\": 5}".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingRequestPart() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Request parts with the following names were not found in the request: another"));
|
||||
new RequestPartFieldsSnippet("another", Arrays.asList(fieldWithPath("a.b").description("one")))
|
||||
.document(new OperationBuilder("missing-request-fields",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.part("part", "{\"a\": {\"b\": 5}}".getBytes()).build());
|
||||
this.thrown.expectMessage(
|
||||
equalTo("A request part named 'another' was not found in the request"));
|
||||
new RequestPartFieldsSnippet("another",
|
||||
Arrays.asList(fieldWithPath("a.b").description("one")))
|
||||
.document(this.operationBuilder.request("http://localhost")
|
||||
.part("part", "{\"a\": {\"b\": 5}}".getBytes()).build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.restdocs.AbstractSnippetTests;
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.restdocs.templates.TemplateFormat;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestPartFieldsSnippet}.
|
||||
*
|
||||
* @author Mathieu Pousse
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestPartFieldsSnippetTests extends AbstractSnippetTests {
|
||||
|
||||
public RequestPartFieldsSnippetTests(String name, TemplateFormat templateFormat) {
|
||||
super(name, templateFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapRequestPartFields() throws IOException {
|
||||
this.snippets.expectRequestPartFields("one")
|
||||
.withContents(tableWithHeader("Path", "Type", "Description")
|
||||
.row("`a.b`", "`Number`", "one").row("`a.c`", "`String`", "two")
|
||||
.row("`a`", "`Object`", "three"));
|
||||
|
||||
new RequestPartFieldsSnippet("one",
|
||||
Arrays.asList(fieldWithPath("a.b").description("one"),
|
||||
fieldWithPath("a.c").description("two"), fieldWithPath("a")
|
||||
.description("three")))
|
||||
.document(
|
||||
this.operationBuilder
|
||||
.request("http://localhost")
|
||||
.part("one",
|
||||
"{\"a\": {\"b\": 5, \"c\": \"charlie\"}}"
|
||||
.getBytes())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleRequestParts() throws IOException {
|
||||
this.snippets.expectRequestPartFields("one");
|
||||
this.snippets.expectRequestPartFields("two");
|
||||
Operation operation = this.operationBuilder.request("http://localhost")
|
||||
.part("one", "{}".getBytes()).and().part("two", "{}".getBytes()).build();
|
||||
new RequestPartFieldsSnippet("one", Collections.<FieldDescriptor>emptyList())
|
||||
.document(operation);
|
||||
new RequestPartFieldsSnippet("two", Collections.<FieldDescriptor>emptyList())
|
||||
.document(operation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allUndocumentedRequestPartFieldsCanBeIgnored() throws IOException {
|
||||
this.snippets.expectRequestPartFields("one")
|
||||
.withContents(tableWithHeader("Path", "Type", "Description").row("`b`",
|
||||
"`Number`", "Field b"));
|
||||
new RequestPartFieldsSnippet("one",
|
||||
Arrays.asList(fieldWithPath("b").description("Field b")), true)
|
||||
.document(this.operationBuilder.request("http://localhost")
|
||||
.part("one", "{\"a\": 5, \"b\": 4}".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalDescriptors() throws IOException {
|
||||
this.snippets.expectRequestPartFields("one")
|
||||
.withContents(tableWithHeader("Path", "Type", "Description")
|
||||
.row("`a.b`", "`Number`", "one").row("`a.c`", "`String`", "two")
|
||||
.row("`a`", "`Object`", "three"));
|
||||
|
||||
PayloadDocumentation
|
||||
.requestPartFields("one", fieldWithPath("a.b").description("one"),
|
||||
fieldWithPath("a.c").description("two"))
|
||||
.and(fieldWithPath("a").description("three"))
|
||||
.document(this.operationBuilder.request("http://localhost")
|
||||
.part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixedAdditionalDescriptors() throws IOException {
|
||||
this.snippets.expectRequestPartFields("one")
|
||||
.withContents(tableWithHeader("Path", "Type", "Description")
|
||||
.row("`a`", "`Object`", "one").row("`a.b`", "`Number`", "two")
|
||||
.row("`a.c`", "`String`", "three"));
|
||||
|
||||
PayloadDocumentation
|
||||
.requestPartFields("one", fieldWithPath("a").description("one"))
|
||||
.andWithPrefix("a.", fieldWithPath("b").description("two"),
|
||||
fieldWithPath("c").description("three"))
|
||||
.document(this.operationBuilder.request("http://localhost")
|
||||
.part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes())
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.restdocs.AbstractSnippetTests;
|
||||
import org.springframework.restdocs.templates.TemplateFormat;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestPartFieldsSnippet}.
|
||||
*
|
||||
* @author Mathieu Pousse
|
||||
*/
|
||||
public class RequestPartsFieldsSnippetTests extends AbstractSnippetTests {
|
||||
|
||||
public RequestPartsFieldsSnippetTests(String name, TemplateFormat templateFormat) {
|
||||
super(name, templateFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapRequestWithFields() throws IOException {
|
||||
this.snippet.expectRequestFields("map-request-parts-with-fields")
|
||||
.withContents(tableWithHeader("Path", "Type", "Description")
|
||||
.row("`a.b`", "`Number`", "one").row("`a.c`", "`String`", "two")
|
||||
.row("`a`", "`Object`", "three"));
|
||||
|
||||
new RequestPartFieldsSnippet("part", Arrays.asList(fieldWithPath("a.b").description("one"),
|
||||
fieldWithPath("a.c").description("two"),
|
||||
fieldWithPath("a").description("three")))
|
||||
.document(operationBuilder("map-request-parts-with-fields")
|
||||
.request("http://localhost")
|
||||
.part("part", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes())
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -81,6 +81,10 @@ public class ExpectedSnippets extends OperationTestRule {
|
||||
return expect("request-fields");
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectRequestPartFields(String partName) {
|
||||
return expect("request-part-" + partName + "-fields");
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectResponseFields() {
|
||||
return expect("response-fields");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user