configuration,
+ RestDocumentationContext context);
}
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractNestedConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractNestedConfigurer.java
new file mode 100644
index 00000000..1463edde
--- /dev/null
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractNestedConfigurer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.config;
+
+/**
+ * Base class for {@link NestedConfigurer} implementations.
+ *
+ * @param The type of the configurer's parent
+ * @author Andy Wilkinson
+ */
+public abstract class AbstractNestedConfigurer
extends AbstractConfigurer implements
+ NestedConfigurer
{
+
+ private final P parent;
+
+ /**
+ * Creates a new {@code AbstractNestedConfigurer} with the given {@code parent}.
+ * @param parent the parent
+ */
+ protected AbstractNestedConfigurer(P parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public final P and() {
+ return this.parent;
+ }
+
+}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/NestedConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/NestedConfigurer.java
similarity index 72%
rename from spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/NestedConfigurer.java
rename to spring-restdocs-core/src/main/java/org/springframework/restdocs/config/NestedConfigurer.java
index 1c6768c8..72e2f41b 100644
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/NestedConfigurer.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/NestedConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,20 @@
* limitations under the License.
*/
-package org.springframework.restdocs.mockmvc;
-
-import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
+package org.springframework.restdocs.config;
/**
* A configurer that is nested and, therefore, has a parent.
*
- * @param The parent's type
+ * @param The parent's type
* @author Andy Wilkinson
*/
-interface NestedConfigurer {
+interface NestedConfigurer {
/**
* Returns the configurer's parent.
*
* @return the parent
*/
- PARENT and();
+ P and();
}
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java
new file mode 100644
index 00000000..bedec85a
--- /dev/null
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.config;
+
+import java.util.Map;
+
+import org.springframework.restdocs.RestDocumentationContext;
+import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver;
+import org.springframework.restdocs.snippet.StandardWriterResolver;
+import org.springframework.restdocs.snippet.WriterResolver;
+import org.springframework.restdocs.templates.StandardTemplateResourceResolver;
+import org.springframework.restdocs.templates.TemplateEngine;
+import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
+
+/**
+ * Abstract base class for the configuration of Spring REST Docs.
+ *
+ * @param The concrete type of the {@link SnippetConfigurer}.
+ * @param The concrete type of this configurer, to be returned from methods that
+ * support chaining
+ * @author Andy Wilkinson
+ */
+public abstract class RestDocumentationConfigurer {
+
+ private final WriterResolverConfigurer writerResolverConfigurer = new WriterResolverConfigurer();
+
+ private final TemplateEngineConfigurer templateEngineConfigurer = new TemplateEngineConfigurer();
+
+ /**
+ * Returns a {@link SnippetConfigurer} that can be used to configure the snippets that
+ * will be generated.
+ *
+ * @return the snippet configurer
+ */
+ public abstract S snippets();
+
+ /**
+ * Configures the {@link TemplateEngine} that will be used for snippet rendering.
+ *
+ * @param templateEngine the template engine to use
+ * @return {@code this}
+ */
+ @SuppressWarnings("unchecked")
+ public final T templateEngine(TemplateEngine templateEngine) {
+ this.templateEngineConfigurer.setTemplateEngine(templateEngine);
+ return (T) this;
+ }
+
+ /**
+ * Configures the {@link WriterResolver} that will be used to resolve a writer for a
+ * snippet.
+ *
+ * @param writerResolver The writer resolver to use
+ * @return {@code this}
+ */
+ @SuppressWarnings("unchecked")
+ public final T writerResolver(WriterResolver writerResolver) {
+ this.writerResolverConfigurer.setWriterResolver(writerResolver);
+ return (T) this;
+ }
+
+ /**
+ * Returns the configurer used to configure the context with a {@link WriterResolver}.
+ *
+ * @return the configurer
+ */
+ protected final AbstractConfigurer getWriterResolverConfigurer() {
+ return this.writerResolverConfigurer;
+ }
+
+ /**
+ * Returns the configurer used to configure the context with a {@link TemplateEngine}.
+ *
+ * @return the configurer
+ */
+ protected final AbstractConfigurer getTemplateEngineConfigurer() {
+ return this.templateEngineConfigurer;
+ }
+
+ private static final class TemplateEngineConfigurer extends AbstractConfigurer {
+
+ private TemplateEngine templateEngine = new MustacheTemplateEngine(
+ new StandardTemplateResourceResolver());
+
+ @Override
+ public void apply(Map configuration,
+ RestDocumentationContext context) {
+ configuration.put(TemplateEngine.class.getName(), this.templateEngine);
+ }
+
+ private void setTemplateEngine(TemplateEngine templateEngine) {
+ this.templateEngine = templateEngine;
+ }
+
+ }
+
+ private static final class WriterResolverConfigurer extends AbstractConfigurer {
+
+ private WriterResolver writerResolver;
+
+ @Override
+ public void apply(Map configuration,
+ RestDocumentationContext context) {
+ WriterResolver resolverToUse = this.writerResolver;
+ if (resolverToUse == null) {
+ resolverToUse = new StandardWriterResolver(
+ new RestDocumentationContextPlaceholderResolver(context));
+ }
+ configuration.put(WriterResolver.class.getName(), resolverToUse);
+ }
+
+ private void setWriterResolver(WriterResolver writerResolver) {
+ this.writerResolver = writerResolver;
+ }
+
+ }
+
+}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/SnippetConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java
similarity index 61%
rename from spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/SnippetConfigurer.java
rename to spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java
index 2723d016..6f4be5a5 100644
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/SnippetConfigurer.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package org.springframework.restdocs.mockmvc;
+package org.springframework.restdocs.config;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
-import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.restdocs.RestDocumentationContext;
import org.springframework.restdocs.curl.CurlDocumentation;
import org.springframework.restdocs.http.HttpDocumentation;
import org.springframework.restdocs.snippet.Snippet;
@@ -28,10 +29,16 @@ import org.springframework.restdocs.snippet.WriterResolver;
/**
* A configurer that can be used to configure the generated documentation snippets.
*
+ * @param The type of the configurer's parent
+ * @param The concrete type of the configurer to be returned from chained methods
* @author Andy Wilkinson
*/
-public class SnippetConfigurer extends
- AbstractNestedConfigurer {
+public abstract class SnippetConfigurer extends AbstractNestedConfigurer
{
+
+ /**
+ * The name of the attribute that is used to hold the default snippets.
+ */
+ public static final String ATTRIBUTE_DEFAULT_SNIPPETS = "org.springframework.restdocs.defaultSnippets";
private List defaultSnippets = Arrays.asList(
CurlDocumentation.curlRequest(), HttpDocumentation.httpRequest(),
@@ -46,10 +53,22 @@ public class SnippetConfigurer extends
private String snippetEncoding = DEFAULT_SNIPPET_ENCODING;
- SnippetConfigurer(RestDocumentationMockMvcConfigurer parent) {
+ /**
+ * Creates a new {@code SnippetConfigurer} with the given {@code parent}.
+ *
+ * @param parent the parent
+ */
+ protected SnippetConfigurer(P parent) {
super(parent);
}
+ @Override
+ public void apply(Map configuration, RestDocumentationContext context) {
+ ((WriterResolver) configuration.get(WriterResolver.class.getName()))
+ .setEncoding(this.snippetEncoding);
+ configuration.put(ATTRIBUTE_DEFAULT_SNIPPETS, this.defaultSnippets);
+ }
+
/**
* Configures any documentation snippets to be written using the given
* {@code encoding}. The default is UTF-8.
@@ -57,17 +76,10 @@ public class SnippetConfigurer extends
* @param encoding the encoding
* @return {@code this}
*/
- public SnippetConfigurer withEncoding(String encoding) {
+ @SuppressWarnings("unchecked")
+ public T withEncoding(String encoding) {
this.snippetEncoding = encoding;
- return this;
- }
-
- @Override
- void apply(MockHttpServletRequest request) {
- ((WriterResolver) request.getAttribute(WriterResolver.class.getName()))
- .setEncoding(this.snippetEncoding);
- request.setAttribute("org.springframework.restdocs.mockmvc.defaultSnippets",
- this.defaultSnippets);
+ return (T) this;
}
/**
@@ -76,8 +88,9 @@ public class SnippetConfigurer extends
* @param defaultSnippets the default snippets
* @return {@code this}
*/
- public SnippetConfigurer withDefaults(Snippet... defaultSnippets) {
+ @SuppressWarnings("unchecked")
+ public T withDefaults(Snippet... defaultSnippets) {
this.defaultSnippets = Arrays.asList(defaultSnippets);
- return this;
+ return (T) this;
}
}
diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java
index 5df99f6f..0670be1d 100644
--- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java
+++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -92,8 +92,9 @@ public class PathParametersSnippet extends AbstractParametersSnippet {
private String extractUrlTemplate(Operation operation) {
String urlTemplate = (String) operation.getAttributes().get(
"org.springframework.restdocs.urlTemplate");
- Assert.notNull(urlTemplate,
- "urlTemplate not found. Did you use RestDocumentationRequestBuilders to "
+ Assert.notNull(
+ urlTemplate,
+ "urlTemplate not found. If you are using MockMvc, did you use RestDocumentationRequestBuilders to "
+ "build the request?");
return urlTemplate;
}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java
index bedd3753..541f380d 100644
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java
+++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,9 +44,9 @@ public abstract class MockMvcRestDocumentation {
* @return the configurer
* @see ConfigurableMockMvcBuilder#apply(MockMvcConfigurer)
*/
- public static RestDocumentationMockMvcConfigurer documentationConfiguration(
+ public static MockMvcRestDocumentationConfigurer documentationConfiguration(
RestDocumentation restDocumentation) {
- return new RestDocumentationMockMvcConfigurer(restDocumentation);
+ return new MockMvcRestDocumentationConfigurer(restDocumentation);
}
/**
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java
new file mode 100644
index 00000000..f9beacac
--- /dev/null
+++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.mockmvc;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.restdocs.RestDocumentation;
+import org.springframework.restdocs.RestDocumentationContext;
+import org.springframework.restdocs.config.AbstractConfigurer;
+import org.springframework.restdocs.config.RestDocumentationConfigurer;
+import org.springframework.test.web.servlet.request.RequestPostProcessor;
+import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
+import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * A MockMvc-specific {@link RestDocumentationConfigurer}.
+ *
+ * @author Andy Wilkinson
+ */
+public class MockMvcRestDocumentationConfigurer
+ extends
+ RestDocumentationConfigurer
+ implements MockMvcConfigurer {
+
+ private final MockMvcSnippetConfigurer snippetConfigurer = new MockMvcSnippetConfigurer(
+ this);
+
+ private final UriConfigurer uriConfigurer = new UriConfigurer(this);
+
+ private final RestDocumentation restDocumentation;
+
+ MockMvcRestDocumentationConfigurer(RestDocumentation restDocumentation) {
+ super();
+ this.restDocumentation = restDocumentation;
+ }
+
+ /**
+ * Returns a {@link UriConfigurer} that can be used to configure the request URIs that
+ * will be documented.
+ *
+ * @return the URI configurer
+ */
+ public UriConfigurer uris() {
+ return this.uriConfigurer;
+ }
+
+ @Override
+ public RequestPostProcessor beforeMockMvcCreated(
+ ConfigurableMockMvcBuilder> builder, WebApplicationContext context) {
+ return new ConfigurerApplyingRequestPostProcessor(this.restDocumentation,
+ Arrays.asList(getTemplateEngineConfigurer(),
+ getWriterResolverConfigurer(), snippets(), this.uriConfigurer));
+ }
+
+ @Override
+ public void afterConfigurerAdded(ConfigurableMockMvcBuilder> builder) {
+ // Nothing to do
+ }
+
+ @Override
+ public MockMvcSnippetConfigurer snippets() {
+ return this.snippetConfigurer;
+ }
+
+ private static final class ConfigurerApplyingRequestPostProcessor implements
+ RequestPostProcessor {
+
+ private final RestDocumentation restDocumentation;
+
+ private final List configurers;
+
+ private ConfigurerApplyingRequestPostProcessor(
+ RestDocumentation restDocumentation, List configurers) {
+ this.restDocumentation = restDocumentation;
+ this.configurers = configurers;
+ }
+
+ @Override
+ public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
+ RestDocumentationContext context = this.restDocumentation.beforeOperation();
+ request.setAttribute(RestDocumentationContext.class.getName(), context);
+ Map configuration = new HashMap<>();
+ configuration.put(MockHttpServletRequest.class.getName(), request);
+ String urlTemplateAttribute = "org.springframework.restdocs.urlTemplate";
+ configuration.put(urlTemplateAttribute,
+ request.getAttribute(urlTemplateAttribute));
+ request.setAttribute("org.springframework.restdocs.configuration",
+ configuration);
+ for (AbstractConfigurer configurer : this.configurers) {
+ configurer.apply(configuration, context);
+ }
+ return request;
+ }
+ }
+
+}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/AbstractNestedConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcSnippetConfigurer.java
similarity index 65%
rename from spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/AbstractNestedConfigurer.java
rename to spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcSnippetConfigurer.java
index a4444e7c..a46302c0 100644
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/AbstractNestedConfigurer.java
+++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcSnippetConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,40 +16,33 @@
package org.springframework.restdocs.mockmvc;
+import org.springframework.restdocs.config.SnippetConfigurer;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.context.WebApplicationContext;
/**
- * Base class for {@link NestedConfigurer} implementations.
+ * A configurer that can be used to configure the generated documentation snippets.
*
- * @param The type of the configurer's parent
* @author Andy Wilkinson
*/
-abstract class AbstractNestedConfigurer extends
- AbstractConfigurer implements NestedConfigurer, MockMvcConfigurer {
+public class MockMvcSnippetConfigurer extends
+ SnippetConfigurer
+ implements MockMvcConfigurer {
- private final PARENT parent;
-
- protected AbstractNestedConfigurer(PARENT parent) {
- this.parent = parent;
- }
-
- @Override
- public PARENT and() {
- return this.parent;
+ MockMvcSnippetConfigurer(MockMvcRestDocumentationConfigurer parent) {
+ super(parent);
}
@Override
public void afterConfigurerAdded(ConfigurableMockMvcBuilder> builder) {
- this.parent.afterConfigurerAdded(builder);
+ and().afterConfigurerAdded(builder);
}
@Override
public RequestPostProcessor beforeMockMvcCreated(
ConfigurableMockMvcBuilder> builder, WebApplicationContext context) {
- return this.parent.beforeMockMvcCreated(builder, context);
+ return and().beforeMockMvcCreated(builder, context);
}
-
}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationMockMvcConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationMockMvcConfigurer.java
deleted file mode 100644
index 20f06771..00000000
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationMockMvcConfigurer.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2014-2015 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.restdocs.mockmvc;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.restdocs.RestDocumentation;
-import org.springframework.restdocs.RestDocumentationContext;
-import org.springframework.restdocs.curl.CurlDocumentation;
-import org.springframework.restdocs.http.HttpDocumentation;
-import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver;
-import org.springframework.restdocs.snippet.StandardWriterResolver;
-import org.springframework.restdocs.snippet.WriterResolver;
-import org.springframework.restdocs.templates.StandardTemplateResourceResolver;
-import org.springframework.restdocs.templates.TemplateEngine;
-import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
-import org.springframework.test.web.servlet.request.RequestPostProcessor;
-import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
-import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
-import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter;
-import org.springframework.web.context.WebApplicationContext;
-
-/**
- * A {@link MockMvcConfigurer} that can be used to configure the documentation.
- *
- * In the absence of any {@link #snippets() customization} the following snippets will be
- * produced by default:
- *
- * - {@link CurlDocumentation#curlRequest() Curl request}
- * - {@link HttpDocumentation#httpRequest() HTTP request}
- * - {@link HttpDocumentation#httpResponse() HTTP response}
- *
- *
- * In the absence of any {@link #uris() customization}, documented URIs have the following
- * defaults:
- *
- * - Scheme: {@code http}
- * - Host: {@code localhost}
- * - Port: {@code 8080}
- *
- *
- * @author Andy Wilkinson
- * @author Dmitriy Mayboroda
- * @see ConfigurableMockMvcBuilder#apply(MockMvcConfigurer)
- * @see MockMvcRestDocumentation#documentationConfiguration(RestDocumentation)
- */
-public class RestDocumentationMockMvcConfigurer extends MockMvcConfigurerAdapter {
-
- private final UriConfigurer uriConfigurer = new UriConfigurer(this);
-
- private final SnippetConfigurer snippetConfigurer = new SnippetConfigurer(this);
-
- private final RequestPostProcessor requestPostProcessor;
-
- private final TemplateEngineConfigurer templateEngineConfigurer = new TemplateEngineConfigurer();
-
- private final WriterResolverConfigurer writerResolverConfigurer = new WriterResolverConfigurer();
-
- /**
- * Creates a new {code RestDocumentationMockMvcConfigurer} that will use the given
- * {@code restDocumentation} when configuring MockMvc.
- *
- * @param restDocumentation the rest documentation
- * @see MockMvcRestDocumentation#documentationConfiguration(RestDocumentation)
- */
- RestDocumentationMockMvcConfigurer(RestDocumentation restDocumentation) {
- this.requestPostProcessor = new ConfigurerApplyingRequestPostProcessor(
- restDocumentation, this.uriConfigurer, this.writerResolverConfigurer,
- this.snippetConfigurer, this.templateEngineConfigurer);
- }
-
- /**
- * Returns a {@link UriConfigurer} that can be used to configure the request URIs that
- * will be documented.
- *
- * @return the URI configurer
- */
- public UriConfigurer uris() {
- return this.uriConfigurer;
- }
-
- /**
- * Returns a {@link SnippetConfigurer} that can be used to configure the snippets that
- * will be generated.
- *
- * @return the snippet configurer
- */
- public SnippetConfigurer snippets() {
- return this.snippetConfigurer;
- }
-
- /**
- * Configures the {@link TemplateEngine} that will be used for snippet rendering.
- *
- * @param templateEngine the template engine to use
- * @return {@code this}
- */
- public RestDocumentationMockMvcConfigurer templateEngine(TemplateEngine templateEngine) {
- this.templateEngineConfigurer.setTemplateEngine(templateEngine);
- return this;
- }
-
- /**
- * Configures the {@link WriterResolver} that will be used to resolve a writer for a
- * snippet.
- *
- * @param writerResolver The writer resolver to use
- * @return {@code this}
- */
- public RestDocumentationMockMvcConfigurer writerResolver(WriterResolver writerResolver) {
- this.writerResolverConfigurer.setWriterResolver(writerResolver);
- return this;
- }
-
- @Override
- public RequestPostProcessor beforeMockMvcCreated(
- ConfigurableMockMvcBuilder> builder, WebApplicationContext context) {
- return this.requestPostProcessor;
- }
-
- private static final class TemplateEngineConfigurer extends AbstractConfigurer {
-
- private TemplateEngine templateEngine = new MustacheTemplateEngine(
- new StandardTemplateResourceResolver());
-
- @Override
- void apply(MockHttpServletRequest request) {
- request.setAttribute(TemplateEngine.class.getName(), this.templateEngine);
- }
-
- void setTemplateEngine(TemplateEngine templateEngine) {
- this.templateEngine = templateEngine;
- }
-
- }
-
- private static final class WriterResolverConfigurer extends AbstractConfigurer {
-
- private WriterResolver writerResolver;
-
- @Override
- void apply(MockHttpServletRequest request) {
- WriterResolver resolverToUse = this.writerResolver;
- if (resolverToUse == null) {
- resolverToUse = new StandardWriterResolver(
- new RestDocumentationContextPlaceholderResolver(
- (RestDocumentationContext) request
- .getAttribute(RestDocumentationContext.class
- .getName())));
- }
- request.setAttribute(WriterResolver.class.getName(), resolverToUse);
- }
-
- void setWriterResolver(WriterResolver writerResolver) {
- this.writerResolver = writerResolver;
- }
-
- }
-
- private static final class ConfigurerApplyingRequestPostProcessor implements
- RequestPostProcessor {
-
- private final RestDocumentation restDocumentation;
-
- private final AbstractConfigurer[] configurers;
-
- private ConfigurerApplyingRequestPostProcessor(
- RestDocumentation restDocumentation, AbstractConfigurer... configurers) {
- this.restDocumentation = restDocumentation;
- this.configurers = configurers;
- }
-
- @Override
- public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
- request.setAttribute(RestDocumentationContext.class.getName(),
- this.restDocumentation.beforeOperation());
- for (AbstractConfigurer configurer : this.configurers) {
- configurer.apply(request);
- }
- return request;
- }
-
- }
-
-}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java
index 425cd2fc..075c162f 100644
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java
+++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.springframework.restdocs.RestDocumentationContext;
+import org.springframework.restdocs.config.SnippetConfigurer;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationResponse;
@@ -33,8 +35,6 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.util.Assert;
-import static org.springframework.restdocs.mockmvc.IterableEnumeration.iterable;
-
/**
* A Spring MVC Test {@code ResultHandler} for documenting RESTful APIs.
*
@@ -85,9 +85,15 @@ public class RestDocumentationResultHandler implements ResultHandler {
@Override
public void handle(MvcResult result) throws Exception {
Map attributes = new HashMap<>();
- for (String name : iterable(result.getRequest().getAttributeNames())) {
- attributes.put(name, result.getRequest().getAttribute(name));
- }
+ attributes.put(RestDocumentationContext.class.getName(), result.getRequest()
+ .getAttribute(RestDocumentationContext.class.getName()));
+ attributes.put("org.springframework.restdocs.urlTemplate", result.getRequest()
+ .getAttribute("org.springframework.restdocs.urlTemplate"));
+ @SuppressWarnings("unchecked")
+ Map configuration = (Map) result.getRequest()
+ .getAttribute("org.springframework.restdocs.configuration");
+ attributes.putAll(configuration);
+
OperationRequest request = this.requestPreprocessor
.preprocess(new MockMvcOperationRequestFactory()
.createOperationRequest(result.getRequest()));
@@ -103,7 +109,7 @@ public class RestDocumentationResultHandler implements ResultHandler {
}
/**
- * Adds the given {@code snippets} such that that are documented when this result
+ * Adds the given {@code snippets} such that they are documented when this result
* handler is called.
*
* @param snippets the snippets to add
@@ -116,9 +122,10 @@ public class RestDocumentationResultHandler implements ResultHandler {
@SuppressWarnings("unchecked")
private List getSnippets(MvcResult result) {
- List combinedSnippets = new ArrayList<>((List) result
- .getRequest().getAttribute(
- "org.springframework.restdocs.mockmvc.defaultSnippets"));
+ List combinedSnippets = new ArrayList<>(
+ (List) ((Map) result.getRequest().getAttribute(
+ "org.springframework.restdocs.configuration"))
+ .get(SnippetConfigurer.ATTRIBUTE_DEFAULT_SNIPPETS));
combinedSnippets.addAll(this.snippets);
return combinedSnippets;
}
diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java
index b7024fd4..b2c3a8c8 100644
--- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java
+++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,15 @@
package org.springframework.restdocs.mockmvc;
+import java.util.Map;
+
import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.restdocs.RestDocumentationContext;
+import org.springframework.restdocs.config.AbstractNestedConfigurer;
+import org.springframework.test.web.servlet.request.RequestPostProcessor;
+import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
+import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
+import org.springframework.web.context.WebApplicationContext;
/**
* A configurer that can be used to configure the documented URIs.
@@ -24,7 +32,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
* @author Andy Wilkinson
*/
public class UriConfigurer extends
- AbstractNestedConfigurer {
+ AbstractNestedConfigurer implements
+ MockMvcConfigurer {
/**
* The default scheme for documented URIs.
@@ -53,7 +62,7 @@ public class UriConfigurer extends
private int port = DEFAULT_PORT;
- UriConfigurer(RestDocumentationMockMvcConfigurer parent) {
+ UriConfigurer(MockMvcRestDocumentationConfigurer parent) {
super(parent);
}
@@ -94,10 +103,23 @@ public class UriConfigurer extends
}
@Override
- void apply(MockHttpServletRequest request) {
+ public void apply(Map configuration, RestDocumentationContext context) {
+ MockHttpServletRequest request = (MockHttpServletRequest) configuration
+ .get(MockHttpServletRequest.class.getName());
request.setScheme(this.scheme);
request.setServerPort(this.port);
request.setServerName(this.host);
}
+ @Override
+ public void afterConfigurerAdded(ConfigurableMockMvcBuilder> builder) {
+ and().afterConfigurerAdded(builder);
+ }
+
+ @Override
+ public RequestPostProcessor beforeMockMvcCreated(
+ ConfigurableMockMvcBuilder> builder, WebApplicationContext context) {
+ return and().beforeMockMvcCreated(builder, context);
+ }
+
}
diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationConfigurerTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java
similarity index 85%
rename from spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationConfigurerTests.java
rename to spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java
index 486bfd8e..ad6ac269 100644
--- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationConfigurerTests.java
+++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,12 +33,12 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
- * Tests for {@link RestDocumentationMockMvcConfigurer}.
+ * Tests for {@link MockMvcRestDocumentationConfigurer}.
*
* @author Andy Wilkinson
* @author Dmitriy Mayboroda
*/
-public class RestDocumentationConfigurerTests {
+public class MockMvcRestDocumentationConfigurerTests {
private MockHttpServletRequest request = new MockHttpServletRequest();
@@ -47,7 +47,7 @@ public class RestDocumentationConfigurerTests {
@Test
public void defaultConfiguration() {
- RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
+ RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(
this.restDocumentation).beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
@@ -56,7 +56,7 @@ public class RestDocumentationConfigurerTests {
@Test
public void customScheme() {
- RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
+ RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(
this.restDocumentation).uris().withScheme("https")
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
@@ -66,7 +66,7 @@ public class RestDocumentationConfigurerTests {
@Test
public void customHost() {
- RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
+ RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(
this.restDocumentation).uris().withHost("api.example.com")
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
@@ -76,7 +76,7 @@ public class RestDocumentationConfigurerTests {
@Test
public void customPort() {
- RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
+ RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(
this.restDocumentation).uris().withPort(8081)
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
@@ -86,7 +86,7 @@ public class RestDocumentationConfigurerTests {
@Test
public void noContentLengthHeaderWhenRequestHasNotContent() {
- RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
+ RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(
this.restDocumentation).uris().withPort(8081)
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java
index 76e3dd67..d444dd17 100644
--- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java
+++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java
@@ -83,7 +83,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
- * Integration tests for using Spring REST Docs with Spring Test's Mock MVC.
+ * Integration tests for using Spring REST Docs with Spring Test's MockMvc.
*
* @author Andy Wilkinson
* @author Dewet Diener
@@ -112,8 +112,10 @@ public class MockMvcRestDocumentationIntegrationTests {
@Test
public void basicSnippetGeneration() throws Exception {
- MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
- .apply(documentationConfiguration(this.restDocumentation)).build();
+ MockMvc mockMvc = MockMvcBuilders
+ .webAppContextSetup(this.context)
+ .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation)
+ .snippets().withEncoding("UTF-8")).build();
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("basic"));
diff --git a/spring-restdocs-restassured/build.gradle b/spring-restdocs-restassured/build.gradle
new file mode 100644
index 00000000..e7c33f79
--- /dev/null
+++ b/spring-restdocs-restassured/build.gradle
@@ -0,0 +1,16 @@
+dependencies {
+ compile project(':spring-restdocs-core')
+ compile 'com.jayway.restassured:rest-assured'
+
+ testCompile 'org.mockito:mockito-core'
+ testCompile 'org.hamcrest:hamcrest-library'
+ testCompile 'org.springframework.hateoas:spring-hateoas'
+ testCompile 'org.springframework.boot:spring-boot-starter-web:1.2.5.RELEASE'
+ testCompile 'org.springframework:spring-test'
+ testCompile project(path: ':spring-restdocs-core', configuration: 'testArtifacts')
+ testRuntime 'commons-logging:commons-logging:1.2'
+}
+
+test {
+ jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=org.springframework.restdocs.*"
+}
\ No newline at end of file
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactory.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactory.java
new file mode 100644
index 00000000..c3ef0b20
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactory.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.operation.OperationRequest;
+import org.springframework.restdocs.operation.OperationRequestFactory;
+import org.springframework.restdocs.operation.OperationRequestPart;
+import org.springframework.restdocs.operation.OperationRequestPartFactory;
+import org.springframework.restdocs.operation.Parameters;
+
+import com.jayway.restassured.response.Header;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.MultiPartSpecification;
+
+/**
+ * A factory for creating an {@link OperationRequest} derived from a REST Assured
+ * {@link FilterableRequestSpecification}.
+ *
+ * @author Andy Wilkinson
+ */
+class RestAssuredOperationRequestFactory {
+
+ OperationRequest createOperationRequest(FilterableRequestSpecification requestSpec) {
+ return new OperationRequestFactory().create(URI.create(requestSpec.getURI()),
+ HttpMethod.valueOf(requestSpec.getMethod().name()),
+ extractContent(requestSpec), extractHeaders(requestSpec),
+ extractParameters(requestSpec), extractParts(requestSpec));
+ }
+
+ private byte[] extractContent(FilterableRequestSpecification requestSpec) {
+ return convertContent(requestSpec.getBody());
+ }
+
+ private byte[] convertContent(Object content) {
+ if (content instanceof String) {
+ return ((String) content).getBytes();
+ }
+ else if (content instanceof byte[]) {
+ return (byte[]) content;
+ }
+ else if (content == null) {
+ return new byte[0];
+ }
+ else {
+ throw new IllegalStateException("Unsupported request content: "
+ + content.getClass().getName());
+ }
+ }
+
+ private HttpHeaders extractHeaders(FilterableRequestSpecification requestSpec) {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ for (Header header : requestSpec.getHeaders()) {
+ httpHeaders.add(header.getName(), header.getValue());
+ }
+ return httpHeaders;
+ }
+
+ private Parameters extractParameters(FilterableRequestSpecification requestSpec) {
+ Parameters parameters = new Parameters();
+ for (Entry entry : requestSpec.getQueryParams().entrySet()) {
+ parameters.add(entry.getKey(), entry.getValue().toString());
+ }
+ for (Entry entry : requestSpec.getRequestParams().entrySet()) {
+ parameters.add(entry.getKey(), entry.getValue().toString());
+ }
+ for (Entry entry : requestSpec.getFormParams().entrySet()) {
+ parameters.add(entry.getKey(), entry.getValue().toString());
+ }
+ return parameters;
+ }
+
+ private Collection extractParts(
+ FilterableRequestSpecification requestSpec) {
+ List parts = new ArrayList<>();
+ for (MultiPartSpecification multiPartSpec : requestSpec.getMultiPartParams()) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(multiPartSpec.getMimeType() == null ? MediaType.TEXT_PLAIN
+ : MediaType.parseMediaType(multiPartSpec.getMimeType()));
+ parts.add(new OperationRequestPartFactory().create(
+ multiPartSpec.getControlName(), multiPartSpec.getFileName(),
+ convertContent(multiPartSpec.getContent()), headers));
+ }
+ return parts;
+ }
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationResponseFactory.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationResponseFactory.java
new file mode 100644
index 00000000..bbcd251e
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationResponseFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.restdocs.operation.OperationResponse;
+import org.springframework.restdocs.operation.OperationResponseFactory;
+
+import com.jayway.restassured.response.Header;
+import com.jayway.restassured.response.Response;
+
+/**
+ * A factory for creating an {@link OperationResponse} derived from a REST Assured
+ * {@link Response}.
+ *
+ * @author Andy Wilkinson
+ */
+class RestAssuredOperationResponseFactory {
+
+ OperationResponse createOperationResponse(Response response) {
+ return new OperationResponseFactory().create(
+ HttpStatus.valueOf(response.getStatusCode()), extractHeaders(response),
+ extractContent(response));
+ }
+
+ private HttpHeaders extractHeaders(Response response) {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ for (Header header : response.getHeaders()) {
+ httpHeaders.add(header.getName(), header.getValue());
+ }
+ return httpHeaders;
+ }
+
+ private byte[] extractContent(Response response) {
+ return response.getBody().asByteArray();
+ }
+
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java
new file mode 100644
index 00000000..447ba1f5
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import org.springframework.restdocs.RestDocumentation;
+import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
+import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
+import org.springframework.restdocs.snippet.Snippet;
+
+/**
+ * Static factory methods for documenting RESTful APIs using REST Assured.
+ *
+ * @author Andy Wilkinson
+ */
+public abstract class RestAssuredRestDocumentation {
+
+ private RestAssuredRestDocumentation() {
+
+ }
+
+ /**
+ * Documents the API call with the given {@code identifier} using the given
+ * {@code snippets}.
+ *
+ * @param identifier an identifier for the API call that is being documented
+ * @param snippets the snippets that will document the API call
+ * @return a {@link RestDocumentationFilter} that will produce the documentation
+ */
+ public static RestDocumentationFilter document(String identifier, Snippet... snippets) {
+ return new RestDocumentationFilter(identifier, snippets);
+ }
+
+ /**
+ * Documents the API call with the given {@code identifier} using the given
+ * {@code snippets} in addition to any default snippets. The given
+ * {@code requestPreprocessor} is applied to the request before it is documented.
+ *
+ * @param identifier an identifier for the API call that is being documented
+ * @param requestPreprocessor the request preprocessor
+ * @param snippets the snippets
+ * @return a {@link RestDocumentationFilter} that will produce the documentation
+ */
+ public static RestDocumentationFilter document(String identifier,
+ OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) {
+ return new RestDocumentationFilter(identifier, requestPreprocessor, snippets);
+ }
+
+ /**
+ * Documents the API call with the given {@code identifier} using the given
+ * {@code snippets} in addition to any default snippets. The given
+ * {@code responsePreprocessor} is applied to the request before it is documented.
+ *
+ * @param identifier an identifier for the API call that is being documented
+ * @param responsePreprocessor the response preprocessor
+ * @param snippets the snippets
+ * @return a {@link RestDocumentationFilter} that will produce the documentation
+ */
+ public static RestDocumentationFilter document(String identifier,
+ OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
+ return new RestDocumentationFilter(identifier, responsePreprocessor, snippets);
+ }
+
+ /**
+ * Documents the API call with the given {@code identifier} using the given
+ * {@code snippets} in addition to any default snippets. The given
+ * {@code requestPreprocessor} and {@code responsePreprocessor} are applied to the
+ * request and response respectively before they are documented.
+ *
+ * @param identifier an identifier for the API call that is being documented
+ * @param requestPreprocessor the request preprocessor
+ * @param responsePreprocessor the response preprocessor
+ * @param snippets the snippets
+ * @return a {@link RestDocumentationFilter} that will produce the documentation
+ */
+ public static RestDocumentationFilter document(String identifier,
+ OperationRequestPreprocessor requestPreprocessor,
+ OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
+ return new RestDocumentationFilter(identifier, requestPreprocessor,
+ responsePreprocessor, snippets);
+ }
+
+ /**
+ * Provides access to a {@link RestAssuredRestDocumentationConfigurer} that can be
+ * used to configure Spring REST Docs using the given {@code restDocumentation}.
+ *
+ * @param restDocumentation the REST documentation
+ * @return the configurer
+ */
+ public static RestAssuredRestDocumentationConfigurer documentationConfiguration(
+ RestDocumentation restDocumentation) {
+ return new RestAssuredRestDocumentationConfigurer(restDocumentation);
+ }
+
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java
new file mode 100644
index 00000000..5dbdb1c0
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.restdocs.RestDocumentation;
+import org.springframework.restdocs.RestDocumentationContext;
+import org.springframework.restdocs.config.AbstractConfigurer;
+import org.springframework.restdocs.config.RestDocumentationConfigurer;
+
+import com.jayway.restassured.filter.Filter;
+import com.jayway.restassured.filter.FilterContext;
+import com.jayway.restassured.response.Response;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.FilterableResponseSpecification;
+
+/**
+ * A REST Assured-specific {@link RestDocumentationConfigurer}.
+ *
+ * @author Andy Wilkinson
+ */
+public final class RestAssuredRestDocumentationConfigurer
+ extends
+ RestDocumentationConfigurer
+ implements Filter {
+
+ private final RestAssuredSnippetConfigurer snippetConfigurer = new RestAssuredSnippetConfigurer(
+ this);
+
+ private final List configurers;
+
+ private final RestDocumentation restDocumentation;
+
+ RestAssuredRestDocumentationConfigurer(RestDocumentation restDocumentation) {
+ this.restDocumentation = restDocumentation;
+ this.configurers = Arrays.asList(getTemplateEngineConfigurer(),
+ getWriterResolverConfigurer(), snippets());
+ }
+
+ @Override
+ public RestAssuredSnippetConfigurer snippets() {
+ return this.snippetConfigurer;
+ }
+
+ @Override
+ public Response filter(FilterableRequestSpecification requestSpec,
+ FilterableResponseSpecification responseSpec, FilterContext filterContext) {
+ RestDocumentationContext context = this.restDocumentation.beforeOperation();
+ filterContext.setValue(RestDocumentationContext.class.getName(), context);
+ Map configuration = new HashMap<>();
+ filterContext.setValue("org.springframework.restdocs.configuration",
+ configuration);
+ for (AbstractConfigurer configurer : this.configurers) {
+ configurer.apply(configuration, context);
+ }
+ return filterContext.next(requestSpec, responseSpec);
+ }
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredSnippetConfigurer.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredSnippetConfigurer.java
new file mode 100644
index 00000000..d91b38a1
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredSnippetConfigurer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import org.springframework.restdocs.config.SnippetConfigurer;
+
+import com.jayway.restassured.filter.Filter;
+import com.jayway.restassured.filter.FilterContext;
+import com.jayway.restassured.response.Response;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.FilterableResponseSpecification;
+
+/**
+ * A configurer that can be used to configure the generated documentation snippets when
+ * using REST Assured.
+ *
+ * @author Andy Wilkinson
+ */
+public final class RestAssuredSnippetConfigurer
+ extends
+ SnippetConfigurer
+ implements Filter {
+
+ RestAssuredSnippetConfigurer(RestAssuredRestDocumentationConfigurer parent) {
+ super(parent);
+ }
+
+ @Override
+ public Response filter(FilterableRequestSpecification requestSpec,
+ FilterableResponseSpecification responseSpec, FilterContext context) {
+ return and().filter(requestSpec, responseSpec, context);
+ }
+
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java
new file mode 100644
index 00000000..3460a99a
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.restdocs.RestDocumentationContext;
+import org.springframework.restdocs.config.SnippetConfigurer;
+import org.springframework.restdocs.operation.Operation;
+import org.springframework.restdocs.operation.OperationRequest;
+import org.springframework.restdocs.operation.OperationResponse;
+import org.springframework.restdocs.operation.StandardOperation;
+import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
+import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
+import org.springframework.restdocs.snippet.Snippet;
+
+import com.jayway.restassured.filter.Filter;
+import com.jayway.restassured.filter.FilterContext;
+import com.jayway.restassured.response.Response;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.FilterableResponseSpecification;
+
+/**
+ * A REST Assured {@link Filter} for documenting RESTful APIs.
+ *
+ * @author Andy Wilkinson
+ */
+public final class RestDocumentationFilter implements Filter {
+
+ private final String identifier;
+
+ private final OperationRequestPreprocessor requestPreprocessor;
+
+ private final OperationResponsePreprocessor responsePreprocessor;
+
+ private final List snippets;
+
+ RestDocumentationFilter(String identifier, Snippet... snippets) {
+ this(identifier, new IdentityOperationRequestPreprocessor(),
+ new IdentityOperationResponsePreprocessor(), snippets);
+ }
+
+ RestDocumentationFilter(String identifier,
+ OperationRequestPreprocessor operationRequestPreprocessor,
+ Snippet... snippets) {
+ this(identifier, operationRequestPreprocessor,
+ new IdentityOperationResponsePreprocessor(), snippets);
+ }
+
+ RestDocumentationFilter(String identifier,
+ OperationResponsePreprocessor operationResponsePreprocessor,
+ Snippet... snippets) {
+ this(identifier, new IdentityOperationRequestPreprocessor(),
+ operationResponsePreprocessor, snippets);
+ }
+
+ RestDocumentationFilter(String identifier,
+ OperationRequestPreprocessor requestPreprocessor,
+ OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
+ this.identifier = identifier;
+ this.requestPreprocessor = requestPreprocessor;
+ this.responsePreprocessor = responsePreprocessor;
+ this.snippets = new ArrayList<>(Arrays.asList(snippets));
+ }
+
+ @Override
+ public Response filter(FilterableRequestSpecification requestSpec,
+ FilterableResponseSpecification responseSpec, FilterContext context) {
+ Response response = context.next(requestSpec, responseSpec);
+
+ OperationRequest operationRequest = this.requestPreprocessor
+ .preprocess(new RestAssuredOperationRequestFactory()
+ .createOperationRequest(requestSpec));
+ OperationResponse operationResponse = this.responsePreprocessor
+ .preprocess(new RestAssuredOperationResponseFactory()
+ .createOperationResponse(response));
+
+ RestDocumentationContext documentationContext = context
+ .getValue(RestDocumentationContext.class.getName());
+
+ Map attributes = new HashMap<>();
+ attributes.put(RestDocumentationContext.class.getName(), documentationContext);
+ attributes.put("org.springframework.restdocs.urlTemplate",
+ requestSpec.getUserDefinedPath());
+ Map configuration = context
+ .getValue("org.springframework.restdocs.configuration");
+ attributes.putAll(configuration);
+
+ Operation operation = new StandardOperation(this.identifier, operationRequest,
+ operationResponse, attributes);
+
+ try {
+ for (Snippet snippet : getSnippets(configuration)) {
+ snippet.document(operation);
+ }
+ }
+ catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ return response;
+ }
+
+ /**
+ * Adds the given {@code snippets} such that they are documented when this result
+ * handler is called.
+ *
+ * @param snippets the snippets to add
+ * @return this {@code RestDocumentationFilter}
+ */
+ public RestDocumentationFilter snippets(Snippet... snippets) {
+ this.snippets.addAll(Arrays.asList(snippets));
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List getSnippets(Map configuration) {
+ List combinedSnippets = new ArrayList<>(
+ (List) configuration
+ .get(SnippetConfigurer.ATTRIBUTE_DEFAULT_SNIPPETS));
+ combinedSnippets.addAll(this.snippets);
+ return combinedSnippets;
+ }
+
+ private static final class IdentityOperationRequestPreprocessor implements
+ OperationRequestPreprocessor {
+
+ @Override
+ public OperationRequest preprocess(OperationRequest request) {
+ return request;
+ }
+
+ }
+
+ private static final class IdentityOperationResponsePreprocessor implements
+ OperationResponsePreprocessor {
+
+ @Override
+ public OperationResponse preprocess(OperationResponse response) {
+ return response;
+ }
+
+ }
+
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/RestAssuredPreprocessors.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/RestAssuredPreprocessors.java
new file mode 100644
index 00000000..5e5c1f9f
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/RestAssuredPreprocessors.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured.operation.preprocess;
+
+import org.springframework.restdocs.operation.Operation;
+import org.springframework.restdocs.operation.OperationRequest;
+import org.springframework.restdocs.operation.OperationResponse;
+
+/**
+ * Static factory methods for creating
+ * {@link org.springframework.restdocs.operation.preprocess.OperationPreprocessor
+ * OperationPreprocessors} for use with REST Assured. They can be applied to an
+ * {@link Operation Operation's} {@link OperationRequest request} or
+ * {@link OperationResponse response} before it is documented.
+ *
+ * @author Andy Wilkinson
+ */
+public abstract class RestAssuredPreprocessors {
+
+ private RestAssuredPreprocessors() {
+
+ }
+
+ /**
+ * Returns a {@code UriModifyingOperationPreprocessor} that will modify URIs in the
+ * request or response by changing one or more of their host, scheme, and port.
+ *
+ * @return the preprocessor
+ */
+ public static UriModifyingOperationPreprocessor modifyUris() {
+ return new UriModifyingOperationPreprocessor();
+ }
+
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/UriModifyingOperationPreprocessor.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/UriModifyingOperationPreprocessor.java
new file mode 100644
index 00000000..bb134d0c
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/UriModifyingOperationPreprocessor.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured.operation.preprocess;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.operation.OperationRequest;
+import org.springframework.restdocs.operation.OperationRequestFactory;
+import org.springframework.restdocs.operation.OperationRequestPart;
+import org.springframework.restdocs.operation.OperationRequestPartFactory;
+import org.springframework.restdocs.operation.OperationResponse;
+import org.springframework.restdocs.operation.OperationResponseFactory;
+import org.springframework.restdocs.operation.preprocess.ContentModifier;
+import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor;
+import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
+import org.springframework.util.StringUtils;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * An {@link OperationPreprocessor} that modifies URIs in the request and in the response
+ * by changing one or more of their host, scheme, and port. URIs in the following
+ * locations are modified:
+ *
+ * - {@link OperationRequest#getUri() Request URI}
+ *
- {@link OperationRequest#getHeaders() Request headers}
+ *
- {@link OperationRequest#getContent() Request content}
+ *
- {@link OperationRequestPart#getHeaders() Request part headers}
+ *
- {@link OperationRequestPart#getContent() Request part content}
+ *
- {@link OperationResponse#getHeaders() Response headers}
+ *
- {@link OperationResponse#getContent() Response content}
+ *
+ *
+ * @author Andy Wilkinson
+ */
+public final class UriModifyingOperationPreprocessor implements OperationPreprocessor {
+
+ private final UriModifyingContentModifier contentModifier = new UriModifyingContentModifier();
+
+ private final OperationPreprocessor contentModifyingDelegate = new ContentModifyingOperationPreprocessor(
+ this.contentModifier);
+
+ private String scheme;
+
+ private String host;
+
+ private String port;
+
+ /**
+ * Modifies the URI to use the given {@code scheme}. {@code null}, the default, will
+ * leave the scheme unchanged.
+ *
+ * @param scheme the scheme
+ * @return {@code this}
+ */
+ public UriModifyingOperationPreprocessor scheme(String scheme) {
+ this.scheme = scheme;
+ this.contentModifier.setScheme(scheme);
+ return this;
+ }
+
+ /**
+ * Modifies the URI to use the given {@code host}. {@code null}, the default, will
+ * leave the host unchanged.
+ *
+ * @param host the host
+ * @return {@code this}
+ */
+ public UriModifyingOperationPreprocessor host(String host) {
+ this.host = host;
+ this.contentModifier.setHost(host);
+ return this;
+ }
+
+ /**
+ * Modifies the URI to use the given {@code port}.
+ *
+ * @param port the port
+ * @return {@code this}
+ */
+ public UriModifyingOperationPreprocessor port(int port) {
+ return port(Integer.toString(port));
+ }
+
+ /**
+ * Removes the port from the URI.
+ *
+ * @return {@code this}
+ */
+ public UriModifyingOperationPreprocessor removePort() {
+ return port("");
+ }
+
+ private UriModifyingOperationPreprocessor port(String port) {
+ this.port = port;
+ this.contentModifier.setPort(port);
+ return this;
+ }
+
+ @Override
+ public OperationRequest preprocess(OperationRequest request) {
+ UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(request.getUri());
+ if (this.scheme != null) {
+ uriBuilder.scheme(this.scheme);
+ }
+ if (this.host != null) {
+ uriBuilder.host(this.host);
+ }
+ if (this.port != null) {
+ if (StringUtils.hasText(this.port)) {
+ uriBuilder.port(this.port);
+ }
+ else {
+ uriBuilder.port(null);
+ }
+ }
+ HttpHeaders modifiedHeaders = modify(request.getHeaders());
+ if (this.host != null) {
+ modifiedHeaders.set(HttpHeaders.HOST, this.host);
+ }
+ return this.contentModifyingDelegate.preprocess(new OperationRequestFactory()
+ .create(uriBuilder.build().toUri(), request.getMethod(),
+ request.getContent(), modifiedHeaders, request.getParameters(),
+ modify(request.getParts())));
+ }
+
+ @Override
+ public OperationResponse preprocess(OperationResponse response) {
+ return this.contentModifyingDelegate.preprocess(new OperationResponseFactory()
+ .create(response.getStatus(), modify(response.getHeaders()),
+ response.getContent()));
+ }
+
+ private HttpHeaders modify(HttpHeaders headers) {
+ HttpHeaders modified = new HttpHeaders();
+ for (Entry> header : headers.entrySet()) {
+ for (String value : header.getValue()) {
+ modified.add(header.getKey(), this.contentModifier.modify(value));
+ }
+ }
+ return modified;
+ }
+
+ private Collection modify(Collection parts) {
+ List modifiedParts = new ArrayList<>();
+ OperationRequestPartFactory factory = new OperationRequestPartFactory();
+ for (OperationRequestPart part : parts) {
+ modifiedParts.add(factory.create(part.getName(), part.getSubmittedFileName(),
+ this.contentModifier.modifyContent(part.getContent(), part
+ .getHeaders().getContentType()), modify(part.getHeaders())));
+ }
+ return modifiedParts;
+ }
+
+ private static final class UriModifyingContentModifier implements ContentModifier {
+
+ private static final Pattern SCHEME_HOST_PORT_PATTERN = Pattern
+ .compile("(http[s]?)://([^/:#?]+)(:[0-9]+)?");
+
+ private String scheme;
+
+ private String host;
+
+ private String port;
+
+ private void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+ private void setHost(String host) {
+ this.host = host;
+ }
+
+ private void setPort(String port) {
+ this.port = port;
+ }
+
+ @Override
+ public byte[] modifyContent(byte[] content, MediaType contentType) {
+ String input;
+ if (contentType != null && contentType.getCharSet() != null) {
+ input = new String(content, contentType.getCharSet());
+ }
+ else {
+ input = new String(content);
+ }
+
+ return modify(input).getBytes();
+ }
+
+ private String modify(String input) {
+ List replacements = Arrays.asList(this.scheme, this.host,
+ StringUtils.hasText(this.port) ? ":" + this.port : this.port);
+
+ int previous = 0;
+
+ Matcher matcher = SCHEME_HOST_PORT_PATTERN.matcher(input);
+ StringBuilder builder = new StringBuilder();
+ while (matcher.find()) {
+ for (int i = 1; i <= matcher.groupCount(); i++) {
+ if (matcher.start(i) >= 0) {
+ builder.append(input.substring(previous, matcher.start(i)));
+ }
+ if (matcher.start(i) >= 0) {
+ previous = matcher.end(i);
+ }
+ builder.append(getReplacement(matcher.group(i),
+ replacements.get(i - 1)));
+ }
+ }
+
+ if (previous < input.length()) {
+ builder.append(input.substring(previous));
+ }
+ return builder.toString();
+ }
+
+ private String getReplacement(String original, String candidate) {
+ if (candidate != null) {
+ return candidate;
+ }
+ if (original != null) {
+ return original;
+ }
+ return "";
+ }
+ }
+}
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/package-info.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/package-info.java
new file mode 100644
index 00000000..83ffc61b
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/operation/preprocess/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * REST Assured-specific support for preprocessing an operation prior to it being
+ * documented.
+ */
+package org.springframework.restdocs.restassured.operation.preprocess;
+
diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/package-info.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/package-info.java
new file mode 100644
index 00000000..a8b893fa
--- /dev/null
+++ b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core classes for using Spring REST Docs with REST Assured.
+ */
+package org.springframework.restdocs.restassured;
+
diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactoryTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactoryTests.java
new file mode 100644
index 00000000..bb532559
--- /dev/null
+++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredOperationRequestFactoryTests.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.operation.OperationRequest;
+import org.springframework.restdocs.operation.OperationRequestPart;
+import org.springframework.restdocs.restassured.RestAssuredOperationRequestFactoryTests.TestApplication;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jayway.restassured.RestAssured;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.RequestSpecification;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests for {@link RestAssuredOperationRequestFactory}.
+ *
+ * @author Andy Wilkinson
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = TestApplication.class)
+@WebAppConfiguration
+@IntegrationTest("server.port=0")
+public class RestAssuredOperationRequestFactoryTests {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private final RestAssuredOperationRequestFactory factory = new RestAssuredOperationRequestFactory();
+
+ @Value("${local.server.port}")
+ private int port;
+
+ @Test
+ public void requestUri() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port);
+ requestSpec.get("/foo/bar");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getUri(),
+ is(equalTo(URI.create("http://localhost:" + this.port + "/foo/bar"))));
+ }
+
+ @Test
+ public void requestMethod() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port);
+ requestSpec.head("/foo/bar");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getMethod(), is(equalTo(HttpMethod.HEAD)));
+ }
+
+ @Test
+ public void queryStringParameters() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port)
+ .queryParam("foo", "bar");
+ requestSpec.get("/");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getParameters().size(), is(1));
+ assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar"))));
+ }
+
+ @Test
+ public void queryStringFromUrlParameters() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port);
+ requestSpec.get("/?foo=bar");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getParameters().size(), is(1));
+ assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar"))));
+ }
+
+ @Test
+ public void formParameters() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port)
+ .formParameter("foo", "bar");
+ requestSpec.get("/");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getParameters().size(), is(1));
+ assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar"))));
+ }
+
+ @Test
+ public void requestParameters() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port)
+ .parameter("foo", "bar");
+ requestSpec.get("/");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getParameters().size(), is(1));
+ assertThat(request.getParameters().get("foo"), is(equalTo(Arrays.asList("bar"))));
+ }
+
+ @Test
+ public void headers() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port)
+ .header("Foo", "bar");
+ requestSpec.get("/");
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getHeaders().toString(), request.getHeaders().size(), is(3));
+ assertThat(request.getHeaders().get("Foo"), is(equalTo(Arrays.asList("bar"))));
+ assertThat(request.getHeaders().get("Accept"), is(equalTo(Arrays.asList("*/*"))));
+ assertThat(request.getHeaders().get("Host"),
+ is(equalTo(Arrays.asList("localhost"))));
+ }
+
+ @Test
+ public void multipart() {
+ RequestSpecification requestSpec = RestAssured.given().port(this.port)
+ .multiPart("a", "a.txt", "alpha", null)
+ .multiPart("b", new ObjectBody("bar"), "application/json");
+ requestSpec.post().then().statusCode(200);
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ Collection parts = request.getParts();
+ assertThat(parts.size(), is(2));
+ Iterator iterator = parts.iterator();
+ OperationRequestPart part = iterator.next();
+ assertThat(part.getName(), is(equalTo("a")));
+ assertThat(part.getSubmittedFileName(), is(equalTo("a.txt")));
+ assertThat(part.getContentAsString(), is(equalTo("alpha")));
+ assertThat(part.getHeaders().getContentType(), is(equalTo(MediaType.TEXT_PLAIN)));
+ part = iterator.next();
+ assertThat(part.getName(), is(equalTo("b")));
+ assertThat(part.getSubmittedFileName(), is(equalTo("file")));
+ assertThat(part.getContentAsString(), is(equalTo("{\"foo\":\"bar\"}")));
+ assertThat(part.getHeaders().getContentType(),
+ is(equalTo(MediaType.APPLICATION_JSON)));
+ }
+
+ @Test
+ public void byteArrayBody() {
+ RequestSpecification requestSpec = RestAssured.given().body("body".getBytes())
+ .port(this.port);
+ requestSpec.post();
+ this.factory.createOperationRequest((FilterableRequestSpecification) requestSpec);
+ }
+
+ @Test
+ public void stringBody() {
+ RequestSpecification requestSpec = RestAssured.given().body("body")
+ .port(this.port);
+ requestSpec.post();
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getContentAsString(), is(equalTo("body")));
+ }
+
+ @Test
+ public void objectBody() {
+ RequestSpecification requestSpec = RestAssured.given()
+ .body(new ObjectBody("bar")).port(this.port);
+ requestSpec.post();
+ OperationRequest request = this.factory
+ .createOperationRequest((FilterableRequestSpecification) requestSpec);
+ assertThat(request.getContentAsString(), is(equalTo("{\"foo\":\"bar\"}")));
+ }
+
+ @Test
+ public void inputStreamBodyIsNotSupported() {
+ RequestSpecification requestSpec = RestAssured.given()
+ .body(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 }))
+ .port(this.port);
+ requestSpec.post();
+ this.thrown
+ .expectMessage(equalTo("Unsupported request content: java.io.ByteArrayInputStream"));
+ this.factory.createOperationRequest((FilterableRequestSpecification) requestSpec);
+ }
+
+ @Test
+ public void fileBodyIsNotSupported() {
+ RequestSpecification requestSpec = RestAssured.given()
+ .body(new File("src/test/resources/body.txt")).port(this.port);
+ requestSpec.post();
+ this.thrown.expectMessage(equalTo("Unsupported request content: java.io.File"));
+ this.factory.createOperationRequest((FilterableRequestSpecification) requestSpec);
+ }
+
+ /**
+ * Minimal test web application.
+ */
+ @Configuration
+ @EnableAutoConfiguration
+ @RestController
+ static class TestApplication {
+
+ @RequestMapping("/")
+ void handle() {
+ }
+
+ }
+
+ /**
+ * Sample object body to verify JSON serialization.
+ */
+ static class ObjectBody {
+
+ private final String foo;
+
+ ObjectBody(String foo) {
+ this.foo = foo;
+ }
+
+ public String getFoo() {
+ return this.foo;
+ }
+
+ }
+
+}
diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java
new file mode 100644
index 00000000..1f7bbbd4
--- /dev/null
+++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.restdocs.restassured;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.springframework.restdocs.RestDocumentation;
+import org.springframework.restdocs.snippet.WriterResolver;
+import org.springframework.restdocs.templates.TemplateEngine;
+
+import com.jayway.restassured.filter.FilterContext;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.FilterableResponseSpecification;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link RestAssuredRestDocumentationConfigurer}.
+ *
+ * @author Andy Wilkinson
+ */
+public class RestAssuredRestDocumentationConfigurerTests {
+
+ @Rule
+ public final RestDocumentation restDocumentation = new RestDocumentation("build");
+
+ private final FilterableRequestSpecification requestSpec = mock(FilterableRequestSpecification.class);
+
+ private final FilterableResponseSpecification responseSpec = mock(FilterableResponseSpecification.class);
+
+ private final FilterContext filterContext = mock(FilterContext.class);
+
+ private final RestAssuredRestDocumentationConfigurer configurer = new RestAssuredRestDocumentationConfigurer(
+ this.restDocumentation);
+
+ @Test
+ public void nextFilterIsCalled() {
+ this.configurer.filter(this.requestSpec, this.responseSpec, this.filterContext);
+ verify(this.filterContext).setValue(
+ eq("org.springframework.restdocs.configuration"), any(Map.class));
+ }
+
+ @Test
+ public void configurationIsAddedToTheContext() {
+ this.configurer.filter(this.requestSpec, this.responseSpec, this.filterContext);
+ @SuppressWarnings("rawtypes")
+ ArgumentCaptor