Merge pull request #592 from clydebarrow
* gh-592: Polish "Add support for documenting request and response cookies" Add support for documenting request and response cookies Closes gh-592
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
||||
</module>
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks">
|
||||
<property name="avoidStaticImportExcludes" value=" org.springframework.restdocs.cli.CliDocumentation.*"/>
|
||||
<property name="avoidStaticImportExcludes" value="org.springframework.restdocs.cli.CliDocumentation.*,
|
||||
org.springframework.restdocs.cookies.CookieDocumentation.*"/>
|
||||
</module>
|
||||
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck">
|
||||
|
||||
@@ -21,6 +21,7 @@ dependencies {
|
||||
testImplementation(project(":spring-restdocs-mockmvc"))
|
||||
testImplementation(project(":spring-restdocs-restassured"))
|
||||
testImplementation(project(":spring-restdocs-webtestclient"))
|
||||
testImplementation("jakarta.servlet:jakarta.servlet-api")
|
||||
testImplementation("jakarta.validation:jakarta.validation-api")
|
||||
testImplementation("junit:junit")
|
||||
testImplementation("org.testng:testng:6.9.10")
|
||||
|
||||
@@ -989,6 +989,58 @@ When documenting HTTP Headers, the test fails if a documented header is not foun
|
||||
|
||||
|
||||
|
||||
[[documenting-your-api-http-cookies]]
|
||||
=== HTTP Cookies
|
||||
|
||||
You can document the cookies in a request or response by using `requestCookies` and `responseCookies`, respectively.
|
||||
The following examples show how to do so:
|
||||
|
||||
[source,java,indent=0,role="primary"]
|
||||
.MockMvc
|
||||
----
|
||||
include::{examples-dir}/com/example/mockmvc/HttpCookies.java[tags=cookies]
|
||||
----
|
||||
<1> Make a GET request with a `JSESSIONID` cookie.
|
||||
<2> Configure Spring REST Docs to produce a snippet describing the request's cookies.
|
||||
Uses the static `requestCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<3> Document the `JSESSIONID` cookie. Uses the static `cookieWithName` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<4> Produce a snippet describing the response's cookies.
|
||||
Uses the static `responseCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
|
||||
[source,java,indent=0,role="secondary"]
|
||||
.WebTestClient
|
||||
----
|
||||
include::{examples-dir}/com/example/webtestclient/HttpCookies.java[tags=cookies]
|
||||
----
|
||||
<1> Make a GET request with a `JSESSIONID` cookie.
|
||||
<2> Configure Spring REST Docs to produce a snippet describing the request's cookies.
|
||||
Uses the static `requestCookies` method on
|
||||
`org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<3> Document the `JSESSIONID` cookie.
|
||||
Uses the static `cookieWithName` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<4> Produce a snippet describing the response's cookies.
|
||||
Uses the static `responseCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
|
||||
[source,java,indent=0,role="secondary"]
|
||||
.REST Assured
|
||||
----
|
||||
include::{examples-dir}/com/example/restassured/HttpCookies.java[tags=cookies]
|
||||
----
|
||||
<1> Configure Spring REST Docs to produce a snippet describing the request's cookies.
|
||||
Uses the static `requestCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<2> Document the `JSESSIONID` cookie.
|
||||
Uses the static `cookieWithName` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<3> Produce a snippet describing the response's cookies.
|
||||
Uses the static `responseCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`.
|
||||
<4> Send a `JSESSIONID` cookie with the request.
|
||||
|
||||
The result is a snippet named `request-cookies.adoc` and a snippet named `response-cookies.adoc`.
|
||||
Each contains a table describing the cookies.
|
||||
|
||||
When documenting HTTP Cookies, the test fails if a documented cookie is not found in the request or response.
|
||||
|
||||
|
||||
|
||||
[[documenting-your-api-reusing-snippets]]
|
||||
=== Reusing Snippets
|
||||
|
||||
|
||||
46
docs/src/test/java/com/example/mockmvc/HttpCookies.java
Normal file
46
docs/src/test/java/com/example/mockmvc/HttpCookies.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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 jakarta.servlet.http.Cookie;
|
||||
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
public class HttpCookies {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
public void cookies() throws Exception {
|
||||
// tag::cookies[]
|
||||
this.mockMvc.perform(get("/").cookie(new Cookie("JSESSIONID", "ACBCDFD0FF93D5BB"))) // <1>
|
||||
.andExpect(status().isOk()).andDo(document("cookies", requestCookies(// <2>
|
||||
cookieWithName("JSESSIONID").description("Session token")), // <3>
|
||||
responseCookies(// <4>
|
||||
cookieWithName("JSESSIONID").description("Updated session token"),
|
||||
cookieWithName("logged_in")
|
||||
.description("Set to true if the user is currently logged in"))));
|
||||
// end::cookies[]
|
||||
}
|
||||
|
||||
}
|
||||
44
docs/src/test/java/com/example/restassured/HttpCookies.java
Normal file
44
docs/src/test/java/com/example/restassured/HttpCookies.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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 io.restassured.RestAssured;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies;
|
||||
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
|
||||
|
||||
public class HttpCookies {
|
||||
|
||||
private RequestSpecification spec;
|
||||
|
||||
public void cookies() {
|
||||
// tag::cookies[]
|
||||
RestAssured.given(this.spec).filter(document("cookies", requestCookies(// <1>
|
||||
cookieWithName("JSESSIONID").description("Saved session token")), // <2>
|
||||
responseCookies(// <3>
|
||||
cookieWithName("logged_in").description("If user is logged in"),
|
||||
cookieWithName("JSESSIONID").description("Updated session token"))))
|
||||
.cookie("JSESSIONID", "ACBCDFD0FF93D5BB") // <4>
|
||||
.when().get("/people").then().assertThat().statusCode(is(200));
|
||||
// end::cookies[]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.webtestclient;
|
||||
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies;
|
||||
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
|
||||
|
||||
public class HttpCookies {
|
||||
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
public void cookies() {
|
||||
// tag::cookies[]
|
||||
this.webTestClient.get().uri("/people").cookie("JSESSIONID", "ACBCDFD0FF93D5BB=") // <1>
|
||||
.exchange().expectStatus().isOk().expectBody().consumeWith(document("cookies", requestCookies(// <2>
|
||||
cookieWithName("JSESSIONID").description("Session token")), // <3>
|
||||
responseCookies(// <4>
|
||||
cookieWithName("JSESSIONID").description("Updated session token"),
|
||||
cookieWithName("logged_in").description("User is logged in"))));
|
||||
// end::cookies[]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.restdocs.snippet.TemplatedSnippet;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that
|
||||
* document a RESTful resource's request or response cookies.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractCookiesSnippet extends TemplatedSnippet {
|
||||
|
||||
private final Map<String, CookieDescriptor> descriptorsByName = new LinkedHashMap<>();
|
||||
|
||||
private final boolean ignoreUndocumentedCookies;
|
||||
|
||||
/**
|
||||
* Creates a new {@code AbstractCookiesSnippet} that will produce a snippet named
|
||||
* {@code <type>-cookies}. The cookies will be documented using the given
|
||||
* {@code descriptors} and the given {@code attributes} will be included in the model
|
||||
* during template rendering.
|
||||
* @param type the type of the cookies
|
||||
* @param descriptors the cookie descriptors
|
||||
* @param attributes the additional attributes
|
||||
* @param ignoreUndocumentedCookies whether undocumented cookies should be ignored
|
||||
*/
|
||||
protected AbstractCookiesSnippet(String type, List<CookieDescriptor> descriptors, Map<String, Object> attributes,
|
||||
boolean ignoreUndocumentedCookies) {
|
||||
super(type + "-cookies", attributes);
|
||||
for (CookieDescriptor descriptor : descriptors) {
|
||||
Assert.notNull(descriptor.getName(), "Cookie descriptors must have a name");
|
||||
if (!descriptor.isIgnored()) {
|
||||
Assert.notNull(descriptor.getDescription(), "The descriptor for cookie '" + descriptor.getName()
|
||||
+ "' must either have a description or be marked as ignored");
|
||||
}
|
||||
this.descriptorsByName.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
this.ignoreUndocumentedCookies = ignoreUndocumentedCookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> createModel(Operation operation) {
|
||||
verifyCookieDescriptors(operation);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
List<Map<String, Object>> cookies = new ArrayList<>();
|
||||
for (CookieDescriptor descriptor : this.descriptorsByName.values()) {
|
||||
if (!descriptor.isIgnored()) {
|
||||
cookies.add(createModelForDescriptor(descriptor));
|
||||
}
|
||||
}
|
||||
model.put("cookies", cookies);
|
||||
return model;
|
||||
}
|
||||
|
||||
private void verifyCookieDescriptors(Operation operation) {
|
||||
Set<String> actualCookies = extractActualCookies(operation);
|
||||
Set<String> expectedCookies = new HashSet<>();
|
||||
for (Entry<String, CookieDescriptor> entry : this.descriptorsByName.entrySet()) {
|
||||
if (!entry.getValue().isOptional()) {
|
||||
expectedCookies.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
Set<String> undocumentedCookies;
|
||||
if (this.ignoreUndocumentedCookies) {
|
||||
undocumentedCookies = Collections.emptySet();
|
||||
}
|
||||
else {
|
||||
undocumentedCookies = new HashSet<>(actualCookies);
|
||||
undocumentedCookies.removeAll(this.descriptorsByName.keySet());
|
||||
}
|
||||
Set<String> missingCookies = new HashSet<>(expectedCookies);
|
||||
missingCookies.removeAll(actualCookies);
|
||||
|
||||
if (!undocumentedCookies.isEmpty() || !missingCookies.isEmpty()) {
|
||||
verificationFailed(undocumentedCookies, missingCookies);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the names of the cookies from the request or response of the given
|
||||
* {@code operation}.
|
||||
* @param operation the operation
|
||||
* @return the cookie names
|
||||
*/
|
||||
protected abstract Set<String> extractActualCookies(Operation operation);
|
||||
|
||||
/**
|
||||
* Called when the documented cookies do not match the actual cookies.
|
||||
* @param undocumentedCookies the cookies that were found in the operation but were
|
||||
* not documented
|
||||
* @param missingCookies the cookies that were documented but were not found in the
|
||||
* operation
|
||||
*/
|
||||
protected abstract void verificationFailed(Set<String> undocumentedCookies, Set<String> missingCookies);
|
||||
|
||||
/**
|
||||
* Returns the list of {@link CookieDescriptor CookieDescriptors} that will be used to
|
||||
* generate the documentation.
|
||||
* @return the cookie descriptors
|
||||
*/
|
||||
protected final Map<String, CookieDescriptor> getCookieDescriptors() {
|
||||
return this.descriptorsByName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this snippet ignores undocumented cookies.
|
||||
* @return {@code true} if undocumented cookies are ignored, otherwise {@code false}
|
||||
*/
|
||||
protected final boolean isIgnoreUndocumentedCookies() {
|
||||
return this.ignoreUndocumentedCookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a model for the given {@code descriptor}.
|
||||
* @param descriptor the descriptor
|
||||
* @return the model
|
||||
*/
|
||||
protected Map<String, Object> createModelForDescriptor(CookieDescriptor descriptor) {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("name", descriptor.getName());
|
||||
model.put("description", descriptor.getDescription());
|
||||
model.put("optional", descriptor.isOptional());
|
||||
model.putAll(descriptor.getAttributes());
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import org.springframework.restdocs.snippet.IgnorableDescriptor;
|
||||
|
||||
/**
|
||||
* A description of a cookie found in a request or response.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.0
|
||||
* @see CookieDocumentation#cookieWithName(String)
|
||||
*/
|
||||
public class CookieDescriptor extends IgnorableDescriptor<CookieDescriptor> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private boolean optional;
|
||||
|
||||
/**
|
||||
* Creates a new {@code CookieDescriptor} describing the cookie with the given
|
||||
* {@code name}.
|
||||
* @param name the name
|
||||
*/
|
||||
protected CookieDescriptor(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the cookie as optional.
|
||||
* @return {@code this}
|
||||
*/
|
||||
public final CookieDescriptor optional() {
|
||||
this.optional = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name for the cookie.
|
||||
* @return the cookie name
|
||||
*/
|
||||
public final String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the described cookie is optional, otherwise {@code false}.
|
||||
* @return {@code true} if the described cookie is optional, otherwise {@code false}
|
||||
*/
|
||||
public final boolean isOptional() {
|
||||
return this.optional;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
|
||||
/**
|
||||
* Static factory methods for documenting a RESTful API's request and response cookies.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class CookieDocumentation {
|
||||
|
||||
private CookieDocumentation() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CookieDescriptor} that describes a cookie with the given
|
||||
* {@code name}.
|
||||
* @param name the name of the cookie
|
||||
* @return a {@code CookieDescriptor} ready for further configuration
|
||||
*/
|
||||
public static CookieDescriptor cookieWithName(String name) {
|
||||
return new CookieDescriptor(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* request. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the request, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the request,
|
||||
* a failure will also occur.
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet requestCookies(CookieDescriptor... descriptors) {
|
||||
return requestCookies(Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* request. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the request, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the request,
|
||||
* a failure will also occur.
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet requestCookies(List<CookieDescriptor> descriptors) {
|
||||
return new RequestCookiesSnippet(descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* request. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* request, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet relaxedRequestCookies(CookieDescriptor... descriptors) {
|
||||
return relaxedRequestCookies(Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* request. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* request, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet relaxedRequestCookies(List<CookieDescriptor> descriptors) {
|
||||
return new RequestCookiesSnippet(descriptors, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's request. The given {@code attributes} will be available during snippet
|
||||
* generation and the cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the request, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the request,
|
||||
* a failure will also occur.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet requestCookies(Map<String, Object> attributes,
|
||||
CookieDescriptor... descriptors) {
|
||||
return requestCookies(attributes, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's request. The given {@code attributes} will be available during snippet
|
||||
* generation and the cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the request, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the request,
|
||||
* a failure will also occur.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet requestCookies(Map<String, Object> attributes,
|
||||
List<CookieDescriptor> descriptors) {
|
||||
return new RequestCookiesSnippet(descriptors, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's request. The given {@code attributes} will be available during snippet
|
||||
* generation and the cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* request, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet relaxedRequestCookies(Map<String, Object> attributes,
|
||||
CookieDescriptor... descriptors) {
|
||||
return relaxedRequestCookies(attributes, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's request. The given {@code attributes} will be available during snippet
|
||||
* generation and the cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* request, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the request's cookies
|
||||
* @return the snippet that will document the request cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static RequestCookiesSnippet relaxedRequestCookies(Map<String, Object> attributes,
|
||||
List<CookieDescriptor> descriptors) {
|
||||
return new RequestCookiesSnippet(descriptors, attributes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* response. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the response, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will also occur.
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet responseCookies(CookieDescriptor... descriptors) {
|
||||
return responseCookies(Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* response. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the response, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will also occur.
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet responseCookies(List<CookieDescriptor> descriptors) {
|
||||
return new ResponseCookiesSnippet(descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* response. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet relaxedResponseCookies(CookieDescriptor... descriptors) {
|
||||
return relaxedResponseCookies(Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API operation's
|
||||
* response. The cookies will be documented using the given {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet relaxedResponseCookies(List<CookieDescriptor> descriptors) {
|
||||
return new ResponseCookiesSnippet(descriptors, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's response. The given {@code attributes} will be available during
|
||||
* snippet generation and the cookies will be documented using the given
|
||||
* {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the response, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will also occur.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet responseCookies(Map<String, Object> attributes,
|
||||
CookieDescriptor... descriptors) {
|
||||
return responseCookies(attributes, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's response. The given {@code attributes} will be available during
|
||||
* snippet generation and the cookies will be documented using the given
|
||||
* {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is present in the response, but is not documented by one of the
|
||||
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
|
||||
* cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will also occur.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet responseCookies(Map<String, Object> attributes,
|
||||
List<CookieDescriptor> descriptors) {
|
||||
return new ResponseCookiesSnippet(descriptors, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's response. The given {@code attributes} will be available during
|
||||
* snippet generation and the cookies will be documented using the given
|
||||
* {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet relaxedResponseCookies(Map<String, Object> attributes,
|
||||
CookieDescriptor... descriptors) {
|
||||
return relaxedResponseCookies(attributes, Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Snippet} that will document the cookies of the API
|
||||
* operations's response. The given {@code attributes} will be available during
|
||||
* snippet generation and the cookies will be documented using the given
|
||||
* {@code descriptors}.
|
||||
* <p>
|
||||
* If a cookie is documented, is not marked as optional, and is not present in the
|
||||
* response, a failure will occur. Any undocumented cookies will be ignored.
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the descriptions of the response's cookies
|
||||
* @return the snippet that will document the response cookies
|
||||
* @see #cookieWithName(String)
|
||||
*/
|
||||
public static ResponseCookiesSnippet relaxedResponseCookies(Map<String, Object> attributes,
|
||||
List<CookieDescriptor> descriptors) {
|
||||
return new ResponseCookiesSnippet(descriptors, attributes, true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.restdocs.operation.RequestCookie;
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
|
||||
/**
|
||||
* A {@link Snippet} that documents the cookies in a request.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.0
|
||||
* @see CookieDocumentation#requestCookies(CookieDescriptor...)
|
||||
* @see CookieDocumentation#requestCookies(Map, CookieDescriptor...)
|
||||
*/
|
||||
public class RequestCookiesSnippet extends AbstractCookiesSnippet {
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestCookiesSnippet} that will document the cookies in the
|
||||
* request using the given {@code descriptors}.
|
||||
* @param descriptors the descriptors
|
||||
*/
|
||||
protected RequestCookiesSnippet(List<CookieDescriptor> descriptors) {
|
||||
this(descriptors, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestCookiesSnippet} that will document the cookies in the
|
||||
* request using the given {@code descriptors}. If {@code ignoreUndocumentedCookies}
|
||||
* is {@code true}, undocumented cookies will be ignored and will not trigger a
|
||||
* failure.
|
||||
* @param descriptors the descriptors
|
||||
* @param ignoreUndocumentedCookies whether undocumented cookies should be ignored
|
||||
*/
|
||||
protected RequestCookiesSnippet(List<CookieDescriptor> descriptors, boolean ignoreUndocumentedCookies) {
|
||||
this(descriptors, null, ignoreUndocumentedCookies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestCookiesSnippet} that will document the cookies in the
|
||||
* request using the given {@code descriptors}. The given {@code attributes} will be
|
||||
* included in the model during template rendering. Undocumented cookies will not be
|
||||
* ignored.
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
*/
|
||||
protected RequestCookiesSnippet(List<CookieDescriptor> descriptors, Map<String, Object> attributes) {
|
||||
this(descriptors, attributes, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestCookiesSnippet} that will document the cookies in the
|
||||
* request using the given {@code descriptors}. The given {@code attributes} will be
|
||||
* included in the model during template rendering.
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
* @param ignoreUndocumentedCookies whether undocumented cookies should be ignored
|
||||
*/
|
||||
protected RequestCookiesSnippet(List<CookieDescriptor> descriptors, Map<String, Object> attributes,
|
||||
boolean ignoreUndocumentedCookies) {
|
||||
super("request", descriptors, attributes, ignoreUndocumentedCookies);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> extractActualCookies(Operation operation) {
|
||||
HashSet<String> actualCookies = new HashSet<>();
|
||||
for (RequestCookie cookie : operation.getRequest().getCookies()) {
|
||||
actualCookies.add(cookie.getName());
|
||||
}
|
||||
return actualCookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verificationFailed(Set<String> undocumentedCookies, Set<String> missingCookies) {
|
||||
String message = "";
|
||||
if (!undocumentedCookies.isEmpty()) {
|
||||
message += "Cookies with the following names were not documented: " + undocumentedCookies;
|
||||
}
|
||||
if (!missingCookies.isEmpty()) {
|
||||
if (message.length() > 0) {
|
||||
message += ". ";
|
||||
}
|
||||
message += "Cookies with the following names were not found in the request: " + missingCookies;
|
||||
}
|
||||
throw new SnippetException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code RequestCookiesSnippet} configured with this snippet's
|
||||
* attributes and its descriptors combined with the given
|
||||
* {@code additionalDescriptors}.
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final RequestCookiesSnippet and(CookieDescriptor... additionalDescriptors) {
|
||||
return and(Arrays.asList(additionalDescriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code RequestCookiesSnippet} configured with this snippet's
|
||||
* attributes and its descriptors combined with the given
|
||||
* {@code additionalDescriptors}.
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final RequestCookiesSnippet and(List<CookieDescriptor> additionalDescriptors) {
|
||||
List<CookieDescriptor> combinedDescriptors = new ArrayList<>(this.getCookieDescriptors().values());
|
||||
combinedDescriptors.addAll(additionalDescriptors);
|
||||
return new RequestCookiesSnippet(combinedDescriptors, getAttributes(), isIgnoreUndocumentedCookies());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
|
||||
/**
|
||||
* A {@link Snippet} that documents the cookies in a response.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.0
|
||||
* @see CookieDocumentation#responseCookies(CookieDescriptor...)
|
||||
* @see CookieDocumentation#responseCookies(Map, CookieDescriptor...)
|
||||
*/
|
||||
public class ResponseCookiesSnippet extends AbstractCookiesSnippet {
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the
|
||||
* response using the given {@code descriptors}.
|
||||
* @param descriptors the descriptors
|
||||
*/
|
||||
protected ResponseCookiesSnippet(List<CookieDescriptor> descriptors) {
|
||||
this(descriptors, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the
|
||||
* response using the given {@code descriptors}. If {@code ignoreUndocumentedCookies}
|
||||
* is {@code true}, undocumented cookies will be ignored and will not trigger a
|
||||
* failure.
|
||||
* @param descriptors the descriptors
|
||||
* @param ignoreUndocumentedCookies whether undocumented cookies should be ignored
|
||||
*/
|
||||
protected ResponseCookiesSnippet(List<CookieDescriptor> descriptors, boolean ignoreUndocumentedCookies) {
|
||||
this(descriptors, null, ignoreUndocumentedCookies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the
|
||||
* response using the given {@code descriptors}. The given {@code attributes} will be
|
||||
* included in the model during template rendering. Undocumented cookies will not be
|
||||
* ignored.
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
*/
|
||||
protected ResponseCookiesSnippet(List<CookieDescriptor> descriptors, Map<String, Object> attributes) {
|
||||
this(descriptors, attributes, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the
|
||||
* response using the given {@code descriptors}. The given {@code attributes} will be
|
||||
* included in the model during template rendering.
|
||||
* @param descriptors the descriptors
|
||||
* @param attributes the additional attributes
|
||||
* @param ignoreUndocumentedCookies whether undocumented cookies should be ignored
|
||||
*/
|
||||
protected ResponseCookiesSnippet(List<CookieDescriptor> descriptors, Map<String, Object> attributes,
|
||||
boolean ignoreUndocumentedCookies) {
|
||||
super("response", descriptors, attributes, ignoreUndocumentedCookies);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> extractActualCookies(Operation operation) {
|
||||
return operation.getResponse().getCookies().stream().map(ResponseCookie::getName).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verificationFailed(Set<String> undocumentedCookies, Set<String> missingCookies) {
|
||||
String message = "";
|
||||
if (!undocumentedCookies.isEmpty()) {
|
||||
message += "Cookies with the following names were not documented: " + undocumentedCookies;
|
||||
}
|
||||
if (!missingCookies.isEmpty()) {
|
||||
if (message.length() > 0) {
|
||||
message += ". ";
|
||||
}
|
||||
message += "Cookies with the following names were not found in the response: " + missingCookies;
|
||||
}
|
||||
throw new SnippetException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code ResponseCookiesSnippet} configured with this snippet's
|
||||
* attributes and its descriptors combined with the given
|
||||
* {@code additionalDescriptors}.
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final ResponseCookiesSnippet and(CookieDescriptor... additionalDescriptors) {
|
||||
return and(Arrays.asList(additionalDescriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code ResponseCookiesSnippet} configured with this snippet's
|
||||
* attributes and its descriptors combined with the given
|
||||
* {@code additionalDescriptors}.
|
||||
* @param additionalDescriptors the additional descriptors
|
||||
* @return the new snippet
|
||||
*/
|
||||
public final ResponseCookiesSnippet and(List<CookieDescriptor> additionalDescriptors) {
|
||||
List<CookieDescriptor> combinedDescriptors = new ArrayList<>(this.getCookieDescriptors().values());
|
||||
combinedDescriptors.addAll(additionalDescriptors);
|
||||
return new ResponseCookiesSnippet(combinedDescriptors, getAttributes(), isIgnoreUndocumentedCookies());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Documenting the cookies of a RESTful API's requests and responses.
|
||||
*/
|
||||
package org.springframework.restdocs.cookies;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.restdocs.operation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@@ -23,6 +25,7 @@ import org.springframework.http.HttpStatus;
|
||||
* The response that was received as part of performing an operation on a RESTful service.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
* @see Operation
|
||||
* @see Operation#getRequest()
|
||||
*/
|
||||
@@ -64,4 +67,12 @@ public interface OperationResponse {
|
||||
*/
|
||||
String getContentAsString();
|
||||
|
||||
/**
|
||||
* Returns the {@link ResponseCookie cookies} returned with the response. If no
|
||||
* cookies were returned an empty collection is returned.
|
||||
* @return the cookies, never {@code null}
|
||||
* @since 3.0
|
||||
*/
|
||||
Collection<ResponseCookie> getCookies();
|
||||
|
||||
}
|
||||
|
||||
@@ -16,26 +16,47 @@
|
||||
|
||||
package org.springframework.restdocs.operation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
/**
|
||||
* A factory for creating {@link OperationResponse OperationResponses}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
public class OperationResponseFactory {
|
||||
|
||||
/**
|
||||
* Creates a new {@link OperationResponse} without cookies. If the response has any
|
||||
* content, the given {@code headers} will be augmented to ensure that they include a
|
||||
* {@code Content-Length} header.
|
||||
* @param status the status of the response
|
||||
* @param headers the request's headers
|
||||
* @param content the content of the request
|
||||
* @return the {@code OperationResponse}
|
||||
*/
|
||||
public OperationResponse create(int status, HttpHeaders headers, byte[] content) {
|
||||
return new StandardOperationResponse(status, augmentHeaders(headers, content), content,
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link OperationResponse}. If the response has any content, the given
|
||||
* {@code headers} will be augmented to ensure that they include a
|
||||
* {@code Content-Length} header.
|
||||
* @param status the status of the response
|
||||
* @param headers the response's headers
|
||||
* @param content the content of the response
|
||||
* @param headers the request's headers
|
||||
* @param content the content of the request
|
||||
* @param cookies the cookies
|
||||
* @return the {@code OperationResponse}
|
||||
* @since 3.0
|
||||
*/
|
||||
public OperationResponse create(int status, HttpHeaders headers, byte[] content) {
|
||||
return new StandardOperationResponse(status, augmentHeaders(headers, content), content);
|
||||
public OperationResponse create(int status, HttpHeaders headers, byte[] content,
|
||||
Collection<ResponseCookie> cookies) {
|
||||
return new StandardOperationResponse(status, augmentHeaders(headers, content), content, cookies);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +70,7 @@ public class OperationResponseFactory {
|
||||
*/
|
||||
public OperationResponse createFrom(OperationResponse original, byte[] newContent) {
|
||||
return new StandardOperationResponse(original.getStatusCode(),
|
||||
getUpdatedHeaders(original.getHeaders(), newContent), newContent);
|
||||
getUpdatedHeaders(original.getHeaders(), newContent), newContent, original.getCookies());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +81,8 @@ public class OperationResponseFactory {
|
||||
* @return the new response with the new headers
|
||||
*/
|
||||
public OperationResponse createFrom(OperationResponse original, HttpHeaders newHeaders) {
|
||||
return new StandardOperationResponse(original.getStatusCode(), newHeaders, original.getContent());
|
||||
return new StandardOperationResponse(original.getStatusCode(), newHeaders, original.getContent(),
|
||||
original.getCookies());
|
||||
}
|
||||
|
||||
private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, byte[] content) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.operation;
|
||||
|
||||
/**
|
||||
* A representation of a Cookie returned in a response.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class ResponseCookie {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResponseCookie} with the given {@code name} and {@code value}.
|
||||
* @param name the name of the cookie
|
||||
* @param value the value of the cookie
|
||||
*/
|
||||
public ResponseCookie(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the cookie.
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the cookie.
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.restdocs.operation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@@ -23,21 +25,26 @@ import org.springframework.http.HttpStatus;
|
||||
* Standard implementation of {@link OperationResponse}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
class StandardOperationResponse extends AbstractOperationMessage implements OperationResponse {
|
||||
|
||||
private final int status;
|
||||
|
||||
private Collection<ResponseCookie> cookies;
|
||||
|
||||
/**
|
||||
* Creates a new response with the given {@code status}, {@code headers}, and
|
||||
* {@code content}.
|
||||
* @param status the status of the response
|
||||
* @param headers the headers of the response
|
||||
* @param content the content of the response
|
||||
* @param cookies any cookies included in the response
|
||||
*/
|
||||
StandardOperationResponse(int status, HttpHeaders headers, byte[] content) {
|
||||
StandardOperationResponse(int status, HttpHeaders headers, byte[] content, Collection<ResponseCookie> cookies) {
|
||||
super(content, headers);
|
||||
this.status = status;
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,4 +57,9 @@ class StandardOperationResponse extends AbstractOperationMessage implements Oper
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResponseCookie> getCookies() {
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class UriModifyingOperationPreprocessor implements OperationPreprocessor
|
||||
@Override
|
||||
public OperationResponse preprocess(OperationResponse response) {
|
||||
return this.contentModifyingDelegate.preprocess(new OperationResponseFactory().create(response.getStatusCode(),
|
||||
modify(response.getHeaders()), response.getContent()));
|
||||
modify(response.getHeaders()), response.getContent(), response.getCookies()));
|
||||
}
|
||||
|
||||
private HttpHeaders modify(HttpHeaders headers) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|===
|
||||
|Name|Description
|
||||
|
||||
{{#cookies}}
|
||||
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
|
||||
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
|
||||
|
||||
{{/cookies}}
|
||||
|===
|
||||
@@ -0,0 +1,9 @@
|
||||
|===
|
||||
|Name|Description
|
||||
|
||||
{{#cookies}}
|
||||
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
|
||||
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
|
||||
|
||||
{{/cookies}}
|
||||
|===
|
||||
@@ -0,0 +1,5 @@
|
||||
Name | Description
|
||||
---- | -----------
|
||||
{{#cookies}}
|
||||
`{{name}}` | {{description}}
|
||||
{{/cookies}}
|
||||
@@ -0,0 +1,5 @@
|
||||
Name | Description
|
||||
---- | -----------
|
||||
{{#cookies}}
|
||||
`{{name}}` | {{description}}
|
||||
{{/cookies}}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.restdocs.testfixtures.OperationBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for failures when rendering {@link RequestCookiesSnippet} due to missing or
|
||||
* undocumented cookies.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestCookiesSnippetFailureTests {
|
||||
|
||||
@Rule
|
||||
public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor());
|
||||
|
||||
@Test
|
||||
public void missingRequestCookie() {
|
||||
assertThatExceptionOfType(SnippetException.class)
|
||||
.isThrownBy(() -> new RequestCookiesSnippet(
|
||||
Collections.singletonList(CookieDocumentation.cookieWithName("JSESSIONID").description("one")))
|
||||
.document(this.operationBuilder.request("http://localhost").build()))
|
||||
.withMessage("Cookies with the following names were not found in the request: [JSESSIONID]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedRequestCookie() {
|
||||
assertThatExceptionOfType(SnippetException.class)
|
||||
.isThrownBy(() -> new RequestCookiesSnippet(Collections.emptyList()).document(this.operationBuilder
|
||||
.request("http://localhost").cookie("JSESSIONID", "1234abcd5678efgh").build()))
|
||||
.withMessageEndingWith("Cookies with the following names were not documented: [JSESSIONID]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
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.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateFormat;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestCookiesSnippet}.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestCookiesSnippetTests extends AbstractSnippetTests {
|
||||
|
||||
public RequestCookiesSnippetTests(String name, TemplateFormat templateFormat) {
|
||||
super(name, templateFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithCookies() throws IOException {
|
||||
new RequestCookiesSnippet(
|
||||
Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")))
|
||||
.document(this.operationBuilder.request("http://localhost").cookie("tz", "Europe%2FLondon")
|
||||
.cookie("logged_in", "true").build());
|
||||
assertThat(this.generatedSnippets.requestCookies())
|
||||
.is(tableWithHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoredRequestCookie() throws IOException {
|
||||
new RequestCookiesSnippet(
|
||||
Arrays.asList(cookieWithName("tz").ignored(), cookieWithName("logged_in").description("two")))
|
||||
.document(this.operationBuilder.request("http://localhost").cookie("tz", "Europe%2FLondon")
|
||||
.cookie("logged_in", "true").build());
|
||||
assertThat(this.generatedSnippets.requestCookies())
|
||||
.is(tableWithHeader("Name", "Description").row("`logged_in`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allUndocumentedCookiesCanBeIgnored() throws IOException {
|
||||
new RequestCookiesSnippet(
|
||||
Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")),
|
||||
true).document(
|
||||
this.operationBuilder.request("http://localhost").cookie("tz", "Europe%2FLondon")
|
||||
.cookie("logged_in", "true").cookie("user_session", "abcd1234efgh5678").build());
|
||||
assertThat(this.generatedSnippets.requestCookies())
|
||||
.is(tableWithHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingOptionalCookie() throws IOException {
|
||||
new RequestCookiesSnippet(Arrays.asList(cookieWithName("tz").description("one").optional(),
|
||||
cookieWithName("logged_in").description("two"))).document(
|
||||
this.operationBuilder.request("http://localhost").cookie("logged_in", "true").build());
|
||||
assertThat(this.generatedSnippets.requestCookies())
|
||||
.is(tableWithHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestCookiesWithCustomAttributes() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("request-cookies"))
|
||||
.willReturn(snippetResource("request-cookies-with-title"));
|
||||
new RequestCookiesSnippet(Collections.singletonList(cookieWithName("tz").description("one")),
|
||||
attributes(key("title").value("Custom title")))
|
||||
.document(this.operationBuilder
|
||||
.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost").cookie("tz", "Europe%2FLondon").build());
|
||||
assertThat(this.generatedSnippets.requestCookies()).contains("Custom title");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestCookiesWithCustomDescriptorAttributes() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("request-cookies"))
|
||||
.willReturn(snippetResource("request-cookies-with-extra-column"));
|
||||
new RequestCookiesSnippet(
|
||||
Arrays.asList(cookieWithName("tz").description("one").attributes(key("foo").value("alpha")),
|
||||
cookieWithName("logged_in").description("two").attributes(key("foo").value("bravo"))))
|
||||
.document(this.operationBuilder
|
||||
.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost").cookie("tz", "Europe%2FLondon")
|
||||
.cookie("logged_in", "true").build());
|
||||
assertThat(this.generatedSnippets.requestCookies()).is(//
|
||||
tableWithHeader("Name", "Description", "Foo").row("tz", "one", "alpha").row("logged_in", "two",
|
||||
"bravo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalDescriptors() throws IOException {
|
||||
new RequestCookiesSnippet(
|
||||
Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")))
|
||||
.and(cookieWithName("user_session").description("three"))
|
||||
.document(this.operationBuilder.request("http://localhost").cookie("tz", "Europe%2FLondon")
|
||||
.cookie("logged_in", "true").cookie("user_session", "abcd1234efgh5678").build());
|
||||
assertThat(this.generatedSnippets.requestCookies()).is(tableWithHeader("Name", "Description").row("`tz`", "one")
|
||||
.row("`logged_in`", "two").row("`user_session`", "three"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalDescriptorsWithRelaxedRequestCookies() throws IOException {
|
||||
new RequestCookiesSnippet(
|
||||
Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")),
|
||||
true).and(cookieWithName("user_session").description("three"))
|
||||
.document(this.operationBuilder.request("http://localhost").cookie("tz", "Europe%2FLondon")
|
||||
.cookie("logged_in", "true").cookie("user_session", "abcd1234efgh5678")
|
||||
.cookie("color_theme", "light").build());
|
||||
assertThat(this.generatedSnippets.requestCookies()).is(tableWithHeader("Name", "Description").row("`tz`", "one")
|
||||
.row("`logged_in`", "two").row("`user_session`", "three"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tableCellContentIsEscapedWhenNecessary() throws IOException {
|
||||
new RequestCookiesSnippet(Collections.singletonList(cookieWithName("Foo|Bar").description("one|two")))
|
||||
.document(this.operationBuilder.request("http://localhost").cookie("Foo|Bar", "baz").build());
|
||||
assertThat(this.generatedSnippets.requestCookies()).is(tableWithHeader("Name", "Description")
|
||||
.row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two")));
|
||||
}
|
||||
|
||||
private String escapeIfNecessary(String input) {
|
||||
if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) {
|
||||
return input;
|
||||
}
|
||||
return input.replace("|", "\\|");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.restdocs.testfixtures.OperationBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
|
||||
|
||||
/**
|
||||
* Tests for failures when rendering {@link ResponseCookiesSnippet} due to missing or
|
||||
* undocumented cookies.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ResponseCookiesSnippetFailureTests {
|
||||
|
||||
@Rule
|
||||
public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor());
|
||||
|
||||
@Test
|
||||
public void missingResponseCookie() {
|
||||
assertThatExceptionOfType(SnippetException.class)
|
||||
.isThrownBy(() -> new ResponseCookiesSnippet(
|
||||
Collections.singletonList(cookieWithName("JSESSIONID").description("one")))
|
||||
.document(this.operationBuilder.response().build()))
|
||||
.withMessage("Cookies with the following names were not found in the response: [JSESSIONID]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedResponseCookie() {
|
||||
assertThatExceptionOfType(SnippetException.class)
|
||||
.isThrownBy(() -> new ResponseCookiesSnippet(Collections.emptyList())
|
||||
.document(this.operationBuilder.response().cookie("JSESSIONID", "1234abcd5678efgh").build()))
|
||||
.withMessageEndingWith("Cookies with the following names were not documented: [JSESSIONID]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.cookies;
|
||||
|
||||
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.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateFormat;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResponseCookiesSnippet}.
|
||||
*
|
||||
* @author Clyde Stubbs
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ResponseCookiesSnippetTests extends AbstractSnippetTests {
|
||||
|
||||
public ResponseCookiesSnippetTests(String name, TemplateFormat templateFormat) {
|
||||
super(name, templateFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseWithCookies() throws IOException {
|
||||
new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one"),
|
||||
cookieWithName("user_session").description("two")))
|
||||
.document(this.operationBuilder.response().cookie("has_recent_activity", "true")
|
||||
.cookie("user_session", "1234abcd5678efgh").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).is(tableWithHeader("Name", "Description")
|
||||
.row("`has_recent_activity`", "one").row("`user_session`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoredResponseCookie() throws IOException {
|
||||
new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").ignored(),
|
||||
cookieWithName("user_session").description("two")))
|
||||
.document(this.operationBuilder.response().cookie("has_recent_activity", "true")
|
||||
.cookie("user_session", "1234abcd5678efgh").build());
|
||||
assertThat(this.generatedSnippets.responseCookies())
|
||||
.is(tableWithHeader("Name", "Description").row("`user_session`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allUndocumentedResponseCookiesCanBeIgnored() throws IOException {
|
||||
new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one"),
|
||||
cookieWithName("user_session").description("two")), true)
|
||||
.document(this.operationBuilder.response().cookie("has_recent_activity", "true")
|
||||
.cookie("user_session", "1234abcd5678efgh").cookie("some_cookie", "value").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).is(tableWithHeader("Name", "Description")
|
||||
.row("`has_recent_activity`", "one").row("`user_session`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingOptionalResponseCookie() throws IOException {
|
||||
new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one").optional(),
|
||||
cookieWithName("user_session").description("two")))
|
||||
.document(this.operationBuilder.response().cookie("user_session", "1234abcd5678efgh").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).is(tableWithHeader("Name", "Description")
|
||||
.row("`has_recent_activity`", "one").row("`user_session`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseCookiesWithCustomAttributes() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("response-cookies"))
|
||||
.willReturn(snippetResource("response-cookies-with-title"));
|
||||
new ResponseCookiesSnippet(Collections.singletonList(cookieWithName("has_recent_activity").description("one")),
|
||||
attributes(key("title").value("Custom title")))
|
||||
.document(this.operationBuilder
|
||||
.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver))
|
||||
.response().cookie("has_recent_activity", "true").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).contains("Custom title");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseCookiesWithCustomDescriptorAttributes() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("response-cookies"))
|
||||
.willReturn(snippetResource("response-cookies-with-extra-column"));
|
||||
new ResponseCookiesSnippet(Arrays.asList(
|
||||
cookieWithName("has_recent_activity").description("one").attributes(key("foo").value("alpha")),
|
||||
cookieWithName("user_session").description("two").attributes(key("foo").value("bravo")),
|
||||
cookieWithName("color_theme").description("three").attributes(key("foo").value("charlie"))))
|
||||
.document(this.operationBuilder
|
||||
.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver))
|
||||
.response().cookie("has_recent_activity", "true")
|
||||
.cookie("user_session", "1234abcd5678efgh").cookie("color_theme", "high_contrast")
|
||||
.build());
|
||||
assertThat(this.generatedSnippets.responseCookies())
|
||||
.is(tableWithHeader("Name", "Description", "Foo").row("has_recent_activity", "one", "alpha")
|
||||
.row("user_session", "two", "bravo").row("color_theme", "three", "charlie"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalDescriptors() throws IOException {
|
||||
CookieDocumentation
|
||||
.responseCookies(cookieWithName("has_recent_activity").description("one"),
|
||||
cookieWithName("user_session").description("two"))
|
||||
.and(cookieWithName("color_theme").description("three"))
|
||||
.document(this.operationBuilder.response().cookie("has_recent_activity", "true")
|
||||
.cookie("user_session", "1234abcd5678efgh").cookie("color_theme", "light").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).is(tableWithHeader("Name", "Description")
|
||||
.row("`has_recent_activity`", "one").row("`user_session`", "two").row("`color_theme`", "three"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalDescriptorsWithRelaxedResponseCookies() throws IOException {
|
||||
CookieDocumentation.relaxedResponseCookies(cookieWithName("has_recent_activity").description("one"))
|
||||
.and(cookieWithName("color_theme").description("two"))
|
||||
.document(this.operationBuilder.response().cookie("has_recent_activity", "true")
|
||||
.cookie("user_session", "1234abcd5678efgh").cookie("color_theme", "light").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).is(
|
||||
tableWithHeader("Name", "Description").row("`has_recent_activity`", "one").row("`color_theme`", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tableCellContentIsEscapedWhenNecessary() throws IOException {
|
||||
new ResponseCookiesSnippet(Collections.singletonList(cookieWithName("Foo|Bar").description("one|two")))
|
||||
.document(this.operationBuilder.response().cookie("Foo|Bar", "baz").build());
|
||||
assertThat(this.generatedSnippets.responseCookies()).is(tableWithHeader("Name", "Description")
|
||||
.row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two")));
|
||||
}
|
||||
|
||||
private String escapeIfNecessary(String input) {
|
||||
if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) {
|
||||
return input;
|
||||
}
|
||||
return input.replace("|", "\\|");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Name|Description|Foo
|
||||
|
||||
{{#cookies}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/cookies}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Name|Description
|
||||
|
||||
{{#cookies}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|
||||
{{/cookies}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Name|Description|Foo
|
||||
|
||||
{{#cookies}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/cookies}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Name|Description
|
||||
|
||||
{{#cookies}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|
||||
{{/cookies}}
|
||||
|===
|
||||
@@ -0,0 +1,5 @@
|
||||
Name | Description | Foo
|
||||
---- | ----------- | ---
|
||||
{{#cookies}}
|
||||
{{name}} | {{description}} | {{foo}}
|
||||
{{/cookies}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{{title}}
|
||||
Name | Description
|
||||
---- | -----------
|
||||
{{#cookies}}
|
||||
{{name}} | {{description}}
|
||||
{{/cookies}}
|
||||
@@ -0,0 +1,5 @@
|
||||
Name | Description | Foo
|
||||
---- | ----------- | ---
|
||||
{{#cookies}}
|
||||
{{name}} | {{description}} | {{foo}}
|
||||
{{/cookies}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{{title}}
|
||||
Name | Description
|
||||
---- | -----------
|
||||
{{#cookies}}
|
||||
{{name}} | {{description}}
|
||||
{{/cookies}}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -70,6 +70,14 @@ public class GeneratedSnippets extends OperationTestRule {
|
||||
return snippet("response-headers");
|
||||
}
|
||||
|
||||
public String requestCookies() {
|
||||
return snippet("request-cookies");
|
||||
}
|
||||
|
||||
public String responseCookies() {
|
||||
return snippet("response-cookies");
|
||||
}
|
||||
|
||||
public String httpRequest() {
|
||||
return snippet("http-request");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -22,8 +22,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
@@ -42,6 +44,7 @@ import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.Parameters;
|
||||
import org.springframework.restdocs.operation.RequestCookie;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
import org.springframework.restdocs.operation.StandardOperation;
|
||||
import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolverFactory;
|
||||
import org.springframework.restdocs.snippet.StandardWriterResolver;
|
||||
@@ -94,7 +97,6 @@ public class OperationBuilder extends OperationTestRule {
|
||||
this.name = operationName;
|
||||
this.outputDirectory = outputDirectory;
|
||||
this.requestBuilder = null;
|
||||
this.requestBuilder = null;
|
||||
this.attributes.clear();
|
||||
}
|
||||
|
||||
@@ -265,10 +267,12 @@ public class OperationBuilder extends OperationTestRule {
|
||||
|
||||
private HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private Set<ResponseCookie> cookies = new HashSet<>();
|
||||
|
||||
private byte[] content = new byte[0];
|
||||
|
||||
private OperationResponse buildResponse() {
|
||||
return new OperationResponseFactory().create(this.status, this.headers, this.content);
|
||||
return new OperationResponseFactory().create(this.status, this.headers, this.content, this.cookies);
|
||||
}
|
||||
|
||||
public OperationResponseBuilder status(int status) {
|
||||
@@ -281,6 +285,11 @@ public class OperationBuilder extends OperationTestRule {
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResponseBuilder cookie(String name, String value) {
|
||||
this.cookies.add(new ResponseCookie(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResponseBuilder content(byte[] content) {
|
||||
this.content = content;
|
||||
return this;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -51,6 +51,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
* {@link MockHttpServletRequest}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
class MockMvcRequestConverter implements RequestConverter<MockHttpServletRequest> {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.restdocs.mockmvc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.Cookie;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -23,6 +28,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.ResponseConverter;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -30,13 +36,27 @@ import org.springframework.util.StringUtils;
|
||||
* {@link MockHttpServletResponse}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
class MockMvcResponseConverter implements ResponseConverter<MockHttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public OperationResponse convert(MockHttpServletResponse mockResponse) {
|
||||
return new OperationResponseFactory().create(mockResponse.getStatus(), extractHeaders(mockResponse),
|
||||
mockResponse.getContentAsByteArray());
|
||||
HttpHeaders headers = extractHeaders(mockResponse);
|
||||
Collection<ResponseCookie> cookies = extractCookies(mockResponse);
|
||||
return new OperationResponseFactory().create(mockResponse.getStatus(), headers,
|
||||
mockResponse.getContentAsByteArray(), cookies);
|
||||
}
|
||||
|
||||
private Collection<ResponseCookie> extractCookies(MockHttpServletResponse mockResponse) {
|
||||
if (mockResponse.getCookies() == null || mockResponse.getCookies().length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ResponseCookie> cookies = new ArrayList<>();
|
||||
for (Cookie cookie : mockResponse.getCookies()) {
|
||||
cookies.add(new ResponseCookie(cookie.getName(), cookie.getValue()));
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
|
||||
private HttpHeaders extractHeaders(MockHttpServletResponse response) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -58,6 +59,9 @@ public class MockMvcResponseConverterTests {
|
||||
assertThat(operationResponse.getHeaders()).hasSize(1);
|
||||
assertThat(operationResponse.getHeaders()).containsEntry(HttpHeaders.SET_COOKIE,
|
||||
Collections.singletonList("name=value; Domain=localhost; HttpOnly"));
|
||||
assertThat(operationResponse.getCookies()).hasSize(1);
|
||||
assertThat(operationResponse.getCookies()).first().extracting(ResponseCookie::getName).isEqualTo("name");
|
||||
assertThat(operationResponse.getCookies()).first().extracting(ResponseCookie::getValue).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.util.StreamUtils;
|
||||
* {@link FilterableRequestSpecification}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
class RestAssuredRequestConverter implements RequestConverter<FilterableRequestSpecification> {
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package org.springframework.restdocs.restassured;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.restassured.http.Header;
|
||||
import io.restassured.response.Response;
|
||||
|
||||
@@ -23,19 +29,34 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.ResponseConverter;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
|
||||
/**
|
||||
* A converter for creating an {@link OperationResponse} from a REST Assured
|
||||
* {@link Response}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
class RestAssuredResponseConverter implements ResponseConverter<Response> {
|
||||
|
||||
@Override
|
||||
public OperationResponse convert(Response response) {
|
||||
HttpHeaders headers = extractHeaders(response);
|
||||
Collection<ResponseCookie> cookies = extractCookies(response, headers);
|
||||
return new OperationResponseFactory().create(response.getStatusCode(), extractHeaders(response),
|
||||
extractContent(response));
|
||||
extractContent(response), cookies);
|
||||
}
|
||||
|
||||
private Collection<ResponseCookie> extractCookies(Response response, HttpHeaders headers) {
|
||||
if (response.getCookies() == null || response.getCookies().size() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ResponseCookie> cookies = new ArrayList<>();
|
||||
for (Map.Entry<String, String> cookie : response.getCookies().entrySet()) {
|
||||
cookies.add(new ResponseCookie(cookie.getKey(), cookie.getValue()));
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
|
||||
private HttpHeaders extractHeaders(Response response) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -17,12 +17,14 @@
|
||||
package org.springframework.restdocs.webtestclient;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.ResponseConverter;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -31,13 +33,15 @@ import org.springframework.util.StringUtils;
|
||||
* {@link ExchangeResult}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Clyde Stubbs
|
||||
*/
|
||||
class WebTestClientResponseConverter implements ResponseConverter<ExchangeResult> {
|
||||
|
||||
@Override
|
||||
public OperationResponse convert(ExchangeResult result) {
|
||||
Collection<ResponseCookie> cookies = extractCookies(result);
|
||||
return new OperationResponseFactory().create(result.getStatus().value(), extractHeaders(result),
|
||||
result.getResponseBodyContent());
|
||||
result.getResponseBodyContent(), cookies);
|
||||
}
|
||||
|
||||
private HttpHeaders extractHeaders(ExchangeResult result) {
|
||||
@@ -50,7 +54,7 @@ class WebTestClientResponseConverter implements ResponseConverter<ExchangeResult
|
||||
return headers;
|
||||
}
|
||||
|
||||
private String generateSetCookieHeader(ResponseCookie cookie) {
|
||||
private String generateSetCookieHeader(org.springframework.http.ResponseCookie cookie) {
|
||||
StringBuilder header = new StringBuilder();
|
||||
header.append(cookie.getName());
|
||||
header.append('=');
|
||||
@@ -71,12 +75,21 @@ class WebTestClientResponseConverter implements ResponseConverter<ExchangeResult
|
||||
return header.toString();
|
||||
}
|
||||
|
||||
private Collection<ResponseCookie> extractCookies(ExchangeResult result) {
|
||||
return result.getResponseCookies().values().stream().flatMap(List::stream).map(this::createResponseCookie)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void appendIfAvailable(StringBuilder header, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
header.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseCookie createResponseCookie(org.springframework.http.ResponseCookie original) {
|
||||
return new ResponseCookie(original.getName(), original.getValue());
|
||||
}
|
||||
|
||||
private void appendIfAvailable(StringBuilder header, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
header.append(name);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -23,8 +23,8 @@ import org.junit.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.ResponseCookie;
|
||||
import org.springframework.test.web.reactive.server.ExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
@@ -62,7 +62,8 @@ public class WebTestClientResponseConverterTests {
|
||||
ExchangeResult result = WebTestClient
|
||||
.bindToRouterFunction(RouterFunctions.route(GET("/foo"),
|
||||
(req) -> ServerResponse.ok()
|
||||
.cookie(ResponseCookie.from("name", "value").domain("localhost").httpOnly(true).build())
|
||||
.cookie(org.springframework.http.ResponseCookie.from("name", "value")
|
||||
.domain("localhost").httpOnly(true).build())
|
||||
.build()))
|
||||
.configureClient().baseUrl("http://localhost").build().get().uri("/foo").exchange().expectBody()
|
||||
.returnResult();
|
||||
@@ -70,6 +71,9 @@ public class WebTestClientResponseConverterTests {
|
||||
assertThat(response.getHeaders()).hasSize(1);
|
||||
assertThat(response.getHeaders()).containsEntry(HttpHeaders.SET_COOKIE,
|
||||
Collections.singletonList("name=value; Domain=localhost; HttpOnly"));
|
||||
assertThat(response.getCookies()).hasSize(1);
|
||||
assertThat(response.getCookies()).first().extracting(ResponseCookie::getName).isEqualTo("name");
|
||||
assertThat(response.getCookies()).first().extracting(ResponseCookie::getValue).isEqualTo("value");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user