diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/ResponseModifier.java b/spring-restdocs/src/main/java/org/springframework/restdocs/ResponseModifier.java index f2415a30..e2002904 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/ResponseModifier.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/ResponseModifier.java @@ -48,18 +48,18 @@ public final class ResponseModifier { /** * Provides a {@link RestDocumentationResultHandler} that can be used to document the * request and modified result. - * @param outputDir The directory to which the documentation will be written + * @param identifier An identifier for the API call that is being documented * @return the result handler that will produce the documentation */ - public RestDocumentationResultHandler andDocument(String outputDir) { - return new ResponseModifyingRestDocumentationResultHandler(outputDir); + public RestDocumentationResultHandler andDocument(String identifier) { + return new ResponseModifyingRestDocumentationResultHandler(identifier); } class ResponseModifyingRestDocumentationResultHandler extends RestDocumentationResultHandler { - public ResponseModifyingRestDocumentationResultHandler(String outputDir) { - super(outputDir); + public ResponseModifyingRestDocumentationResultHandler(String identifier) { + super(identifier); } @Override diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java index d3a5226d..58f279d1 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java @@ -47,15 +47,15 @@ public abstract class RestDocumentation { } /** - * Documents the API call to the given {@code outputDir}. + * Documents the API call using the given {@code identifier}. * - * @param outputDir The directory to which the documentation will be written + * @param identifier An identifier for the API call that is being documented * @return a Mock MVC {@code ResultHandler} that will produce the documentation * @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) * @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) */ - public static RestDocumentationResultHandler document(String outputDir) { - return new RestDocumentationResultHandler(outputDir); + public static RestDocumentationResultHandler document(String identifier) { + return new RestDocumentationResultHandler(identifier); } /** diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java index 6d26ba63..1d8a0445 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java @@ -49,7 +49,7 @@ import org.springframework.test.web.servlet.ResultHandler; */ public class RestDocumentationResultHandler implements ResultHandler { - private final String outputDir; + private final String identifier; private SnippetWritingResultHandler curlRequest; @@ -59,11 +59,11 @@ public class RestDocumentationResultHandler implements ResultHandler { private List delegates = new ArrayList<>(); - RestDocumentationResultHandler(String outputDir) { - this.outputDir = outputDir; - this.curlRequest = documentCurlRequest(this.outputDir, null); - this.httpRequest = documentHttpRequest(this.outputDir, null); - this.httpResponse = documentHttpResponse(this.outputDir, null); + RestDocumentationResultHandler(String identifier) { + this.identifier = identifier; + this.curlRequest = documentCurlRequest(this.identifier, null); + this.httpRequest = documentHttpRequest(this.identifier, null); + this.httpResponse = documentHttpResponse(this.identifier, null); } /** @@ -74,7 +74,7 @@ public class RestDocumentationResultHandler implements ResultHandler { * @return {@code this} */ public RestDocumentationResultHandler withCurlRequest(Map attributes) { - this.curlRequest = documentCurlRequest(this.outputDir, attributes); + this.curlRequest = documentCurlRequest(this.identifier, attributes); return this; } @@ -86,7 +86,7 @@ public class RestDocumentationResultHandler implements ResultHandler { * @return {@code this} */ public RestDocumentationResultHandler withHttpRequest(Map attributes) { - this.httpRequest = documentHttpRequest(this.outputDir, attributes); + this.httpRequest = documentHttpRequest(this.identifier, attributes); return this; } @@ -98,7 +98,7 @@ public class RestDocumentationResultHandler implements ResultHandler { * @return {@code this} */ public RestDocumentationResultHandler withHttpResponse(Map attributes) { - this.httpResponse = documentHttpResponse(this.outputDir, attributes); + this.httpResponse = documentHttpResponse(this.identifier, attributes); return this; } @@ -178,7 +178,7 @@ public class RestDocumentationResultHandler implements ResultHandler { */ public RestDocumentationResultHandler withLinks(Map attributes, LinkExtractor linkExtractor, LinkDescriptor... descriptors) { - this.delegates.add(documentLinks(this.outputDir, attributes, linkExtractor, + this.delegates.add(documentLinks(this.identifier, attributes, linkExtractor, descriptors)); return this; } @@ -222,7 +222,7 @@ public class RestDocumentationResultHandler implements ResultHandler { public RestDocumentationResultHandler withRequestFields( Map attributes, FieldDescriptor... descriptors) { this.delegates - .add(documentRequestFields(this.outputDir, attributes, descriptors)); + .add(documentRequestFields(this.identifier, attributes, descriptors)); return this; } @@ -264,8 +264,8 @@ public class RestDocumentationResultHandler implements ResultHandler { */ public RestDocumentationResultHandler withResponseFields( Map attributes, FieldDescriptor... descriptors) { - this.delegates - .add(documentResponseFields(this.outputDir, attributes, descriptors)); + this.delegates.add(documentResponseFields(this.identifier, attributes, + descriptors)); return this; } @@ -304,7 +304,7 @@ public class RestDocumentationResultHandler implements ResultHandler { */ public RestDocumentationResultHandler withQueryParameters( Map attributes, ParameterDescriptor... descriptors) { - this.delegates.add(documentQueryParameters(this.outputDir, attributes, + this.delegates.add(documentQueryParameters(this.identifier, attributes, descriptors)); return this; } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java index c8757d18..0977b68d 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java @@ -21,6 +21,9 @@ import java.util.List; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.restdocs.RestDocumentation; +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; @@ -49,6 +52,8 @@ public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { private TemplateEngineConfigurer templateEngineConfigurer = new TemplateEngineConfigurer(); + private WriterResolverConfigurer writerResolverConfigurer = new WriterResolverConfigurer(); + /** * Creates a new {@link RestDocumentationConfigurer}. * @see RestDocumentation#documentationConfiguration() @@ -56,9 +61,9 @@ public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { public RestDocumentationConfigurer() { this.requestPostProcessor = new ConfigurerApplyingRequestPostProcessor( Arrays. asList(this.uriConfigurer, - this.snippetConfigurer, new StepCountConfigurer(), - new ContentLengthHeaderConfigurer(), - new TemplateEngineConfigurer())); + this.writerResolverConfigurer, this.snippetConfigurer, + new StepCountConfigurer(), new ContentLengthHeaderConfigurer(), + this.templateEngineConfigurer)); } public UriConfigurer uris() { @@ -74,6 +79,11 @@ public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { return this; } + public RestDocumentationConfigurer writerResolver(WriterResolver writerResolver) { + this.writerResolverConfigurer.setWriterResolver(writerResolver); + return this; + } + @Override public RequestPostProcessor beforeMockMvcCreated( ConfigurableMockMvcBuilder builder, WebApplicationContext context) { @@ -84,10 +94,10 @@ public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { @Override void apply(MockHttpServletRequest request) { - RestDocumentationContext currentContext = RestDocumentationContext - .currentContext(); - if (currentContext != null) { - currentContext.getAndIncrementStepCount(); + RestDocumentationContext context = (RestDocumentationContext) request + .getAttribute(RestDocumentationContext.class.getName()); + if (context != null) { + context.getAndIncrementStepCount(); } } @@ -122,6 +132,29 @@ public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { } + private static 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 class ConfigurerApplyingRequestPostProcessor implements RequestPostProcessor { @@ -134,6 +167,8 @@ public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { @Override public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { + request.setAttribute(RestDocumentationContext.class.getName(), + RestDocumentationContextHolder.getCurrentContext()); for (AbstractConfigurer configurer : this.configurers) { configurer.apply(request); } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContext.java b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContext.java index 2f48619d..f5b3f2dc 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContext.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContext.java @@ -19,42 +19,40 @@ package org.springframework.restdocs.config; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; +import org.springframework.test.context.TestContext; + /** * {@code RestDocumentationContext} encapsulates the context in which the documentation of * a RESTful API is being performed. - * + * * @author Andy Wilkinson */ public final class RestDocumentationContext { - private static final ThreadLocal CONTEXTS = new InheritableThreadLocal(); - private final AtomicInteger stepCount = new AtomicInteger(0); - private final Method testMethod; + private final TestContext testContext; - private String snippetEncoding; - - private RestDocumentationContext() { + public RestDocumentationContext() { this(null); } - private RestDocumentationContext(Method testMethod) { - this.testMethod = testMethod; + public RestDocumentationContext(TestContext testContext) { + this.testContext = testContext; } /** * Returns the test {@link Method method} that is currently executing - * + * * @return The test method */ public Method getTestMethod() { - return this.testMethod; + return this.testContext == null ? null : this.testContext.getTestMethod(); } /** * Gets and then increments the current step count - * + * * @return The step count prior to it being incremented */ int getAndIncrementStepCount() { @@ -63,41 +61,11 @@ public final class RestDocumentationContext { /** * Gets the current step count - * + * * @return The current step count */ public int getStepCount() { return this.stepCount.get(); } - void setSnippetEncoding(String snippetEncoding) { - this.snippetEncoding = snippetEncoding; - } - - /** - * Gets the encoding to be used when writing snippets - * - * @return The snippet encoding - */ - public String getSnippetEncoding() { - return this.snippetEncoding; - } - - static void establishContext(Method testMethod) { - CONTEXTS.set(new RestDocumentationContext(testMethod)); - } - - static void clearContext() { - CONTEXTS.set(null); - } - - /** - * Returns the current context, never {@code null}. - * - * @return The current context - */ - public static RestDocumentationContext currentContext() { - return CONTEXTS.get(); - } - } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContextHolder.java b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContextHolder.java new file mode 100644 index 00000000..cefb3fbb --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContextHolder.java @@ -0,0 +1,42 @@ +/* + * 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.config; + +import org.springframework.core.NamedInheritableThreadLocal; + +final class RestDocumentationContextHolder { + + private static final NamedInheritableThreadLocal currentContext = new NamedInheritableThreadLocal<>( + "REST Documentation Context"); + + private RestDocumentationContextHolder() { + + } + + static RestDocumentationContext getCurrentContext() { + return currentContext.get(); + } + + static void setCurrentContext(RestDocumentationContext context) { + currentContext.set(context); + } + + static void removeCurrentContext() { + currentContext.remove(); + } + +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationTestExecutionListener.java b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationTestExecutionListener.java index b685699b..7189ad0a 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationTestExecutionListener.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationTestExecutionListener.java @@ -30,11 +30,12 @@ public class RestDocumentationTestExecutionListener extends AbstractTestExecutio @Override public void beforeTestMethod(TestContext testContext) throws Exception { - RestDocumentationContext.establishContext(testContext.getTestMethod()); + RestDocumentationContextHolder.setCurrentContext(new RestDocumentationContext( + testContext)); } @Override public void afterTestMethod(TestContext testContext) throws Exception { - RestDocumentationContext.clearContext(); + RestDocumentationContextHolder.removeCurrentContext(); } } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java b/spring-restdocs/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java index e94cb331..ae992f7f 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java @@ -17,6 +17,7 @@ package org.springframework.restdocs.config; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.restdocs.snippet.WriterResolver; /** * A configurer that can be used to configure the generated documentation snippets. @@ -52,10 +53,7 @@ public class SnippetConfigurer extends @Override void apply(MockHttpServletRequest request) { - RestDocumentationContext context = RestDocumentationContext.currentContext(); - if (context != null) { - context.setSnippetEncoding(this.snippetEncoding); - } + ((WriterResolver) request.getAttribute(WriterResolver.class.getName())) + .setEncoding(this.snippetEncoding); } - } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java index de7ac531..fbb410fc 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java @@ -49,14 +49,14 @@ public abstract class CurlDocumentation { /** * Produces a documentation snippet containing the request formatted as a cURL command * - * @param outputDir The directory to which snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the curl request * snippet * @return the handler that will produce the snippet */ - public static SnippetWritingResultHandler documentCurlRequest(String outputDir, + public static SnippetWritingResultHandler documentCurlRequest(String identifier, Map attributes) { - return new CurlRequestWritingResultHandler(outputDir, attributes); + return new CurlRequestWritingResultHandler(identifier, attributes); } private static final class CurlRequestWritingResultHandler extends @@ -70,9 +70,9 @@ public abstract class CurlDocumentation { private static final int STANDARD_PORT_HTTPS = 443; - private CurlRequestWritingResultHandler(String outputDir, + private CurlRequestWritingResultHandler(String identifier, Map attributes) { - super(outputDir, "curl-request", attributes); + super(identifier, "curl-request", attributes); } @Override diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java index 44690f81..fd9bdc2f 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java @@ -53,37 +53,37 @@ public abstract class HttpDocumentation { * Produces a documentation snippet containing the request formatted as an HTTP * request * - * @param outputDir The directory to which snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the HTTP requst * snippet * @return the handler that will produce the snippet */ - public static SnippetWritingResultHandler documentHttpRequest(String outputDir, + public static SnippetWritingResultHandler documentHttpRequest(String identifier, Map attributes) { - return new HttpRequestWritingResultHandler(outputDir, attributes); + return new HttpRequestWritingResultHandler(identifier, attributes); } /** * Produces a documentation snippet containing the response formatted as the HTTP * response sent by the server * - * @param outputDir The directory to which snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the HTTP response * snippet * @return the handler that will produce the snippet */ - public static SnippetWritingResultHandler documentHttpResponse(String outputDir, + public static SnippetWritingResultHandler documentHttpResponse(String identifier, Map attributes) { - return new HttpResponseWritingResultHandler(outputDir, attributes); + return new HttpResponseWritingResultHandler(identifier, attributes); } private static final class HttpRequestWritingResultHandler extends SnippetWritingResultHandler { - private HttpRequestWritingResultHandler(String outputDir, + private HttpRequestWritingResultHandler(String identifier, Map attributes) { - super(outputDir, "http-request", attributes); + super(identifier, "http-request", attributes); } @Override @@ -219,9 +219,9 @@ public abstract class HttpDocumentation { private static final class HttpResponseWritingResultHandler extends SnippetWritingResultHandler { - private HttpResponseWritingResultHandler(String outputDir, + private HttpResponseWritingResultHandler(String identifier, Map attributes) { - super(outputDir, "http-response", attributes); + super(identifier, "http-response", attributes); } @Override diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java index 9f919974..4ea7df09 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java @@ -48,7 +48,7 @@ public abstract class HypermediaDocumentation { * Creates a {@code LinkSnippetResultHandler} that will produce a documentation * snippet for a response's links. * - * @param outputDir The directory to which the snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the links snippet * @param linkExtractor Used to extract the links from the response * @param descriptors The descriptions of the response's links @@ -56,10 +56,10 @@ public abstract class HypermediaDocumentation { * @see RestDocumentationResultHandler#withLinks(LinkDescriptor...) * @see RestDocumentationResultHandler#withLinks(LinkExtractor, LinkDescriptor...) */ - public static LinkSnippetResultHandler documentLinks(String outputDir, + public static LinkSnippetResultHandler documentLinks(String identifier, Map attributes, LinkExtractor linkExtractor, LinkDescriptor... descriptors) { - return new LinkSnippetResultHandler(outputDir, attributes, linkExtractor, + return new LinkSnippetResultHandler(identifier, attributes, linkExtractor, Arrays.asList(descriptors)); } } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java index ecc78dbb..cf0f9899 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java @@ -47,9 +47,9 @@ public class LinkSnippetResultHandler extends SnippetWritingResultHandler { private final LinkExtractor extractor; - LinkSnippetResultHandler(String outputDir, Map attributes, + LinkSnippetResultHandler(String identifier, Map attributes, LinkExtractor linkExtractor, List descriptors) { - super(outputDir, "links", attributes); + super(identifier, "links", attributes); this.extractor = linkExtractor; for (LinkDescriptor descriptor : descriptors) { Assert.hasText(descriptor.getRel()); diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/FieldSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/FieldSnippetResultHandler.java index bb08851b..3bc1701f 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/FieldSnippetResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/FieldSnippetResultHandler.java @@ -54,9 +54,9 @@ public abstract class FieldSnippetResultHandler extends SnippetWritingResultHand private List fieldDescriptors; - FieldSnippetResultHandler(String outputDir, String type, + FieldSnippetResultHandler(String identifier, String type, Map attributes, List descriptors) { - super(outputDir, type + "-fields", attributes); + super(identifier, type + "-fields", attributes); this.templateName = type + "-fields"; for (FieldDescriptor descriptor : descriptors) { Assert.notNull(descriptor.getPath()); diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java index e49a5972..4e9d34c6 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java @@ -107,16 +107,16 @@ public abstract class PayloadDocumentation { * field is sufficient for all of its descendants to also be treated as having been * documented. * - * @param outputDir The directory to which the snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the links snippet * @param descriptors The descriptions of the request's fields * @return the handler * @see RestDocumentationResultHandler#withRequestFields(FieldDescriptor...) * @see #fieldWithPath(String) */ - public static FieldSnippetResultHandler documentRequestFields(String outputDir, + public static FieldSnippetResultHandler documentRequestFields(String identifier, Map attributes, FieldDescriptor... descriptors) { - return new RequestFieldSnippetResultHandler(outputDir, attributes, + return new RequestFieldSnippetResultHandler(identifier, attributes, Arrays.asList(descriptors)); } @@ -131,15 +131,15 @@ public abstract class PayloadDocumentation { * field is sufficient for all of its descendants to also be treated as having been * documented. * - * @param outputDir The directory to which the snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the links snippet * @param descriptors The descriptions of the response's fields * @return the handler * @see RestDocumentationResultHandler#withResponseFields(FieldDescriptor...) */ - public static FieldSnippetResultHandler documentResponseFields(String outputDir, + public static FieldSnippetResultHandler documentResponseFields(String identifier, Map attributes, FieldDescriptor... descriptors) { - return new ResponseFieldSnippetResultHandler(outputDir, attributes, + return new ResponseFieldSnippetResultHandler(identifier, attributes, Arrays.asList(descriptors)); } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/RequestFieldSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/RequestFieldSnippetResultHandler.java index aad0f6e0..851204d2 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/RequestFieldSnippetResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/RequestFieldSnippetResultHandler.java @@ -29,9 +29,9 @@ import org.springframework.test.web.servlet.MvcResult; */ public class RequestFieldSnippetResultHandler extends FieldSnippetResultHandler { - RequestFieldSnippetResultHandler(String outputDir, Map attributes, + RequestFieldSnippetResultHandler(String identifier, Map attributes, List descriptors) { - super(outputDir, "request", attributes, descriptors); + super(identifier, "request", attributes, descriptors); } @Override diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/ResponseFieldSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/ResponseFieldSnippetResultHandler.java index 76ff2c79..e5af663c 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/payload/ResponseFieldSnippetResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/payload/ResponseFieldSnippetResultHandler.java @@ -30,9 +30,9 @@ import org.springframework.test.web.servlet.MvcResult; */ public class ResponseFieldSnippetResultHandler extends FieldSnippetResultHandler { - ResponseFieldSnippetResultHandler(String outputDir, Map attributes, + ResponseFieldSnippetResultHandler(String identifier, Map attributes, List descriptors) { - super(outputDir, "response", attributes, descriptors); + super(identifier, "response", attributes, descriptors); } @Override diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java index bbbb3e70..ad45e62b 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java @@ -43,9 +43,9 @@ public class QueryParametersSnippetResultHandler extends SnippetWritingResultHan private final Map descriptorsByName = new LinkedHashMap<>(); - protected QueryParametersSnippetResultHandler(String outputDir, + protected QueryParametersSnippetResultHandler(String identifier, Map attributes, ParameterDescriptor... descriptors) { - super(outputDir, "query-parameters", attributes); + super(identifier, "query-parameters", attributes); for (ParameterDescriptor descriptor : descriptors) { Assert.hasText(descriptor.getName()); Assert.hasText(descriptor.getDescription()); diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java index 6fc061c7..aadcf76a 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java @@ -36,16 +36,17 @@ public abstract class RequestDocumentation { * Creates a {@link SnippetWritingResultHandler} that will produce a snippet * documenting a request's query parameters * - * @param outputDir The directory to which the snippet should be written + * @param identifier An identifier for the API call that is being documented * @param attributes Attributes made available during rendering of the query * parameters snippet * @param descriptors The descriptions of the parameters in the request's query string * @return the result handler * @see RestDocumentationResultHandler#withQueryParameters(ParameterDescriptor...) */ - public static SnippetWritingResultHandler documentQueryParameters(String outputDir, + public static SnippetWritingResultHandler documentQueryParameters(String identifier, Map attributes, ParameterDescriptor... descriptors) { - return new QueryParametersSnippetResultHandler(outputDir, attributes, descriptors); + return new QueryParametersSnippetResultHandler(identifier, attributes, + descriptors); } /** diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/OutputFileResolver.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/OutputFileResolver.java deleted file mode 100644 index 86b9face..00000000 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/OutputFileResolver.java +++ /dev/null @@ -1,101 +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.snippet; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.restdocs.config.RestDocumentationContext; - -/** - * {@code OutputFileResolver} resolves an absolute output file based on the current - * configuration and context. - * - * @author Andy Wilkinson - */ -class OutputFileResolver { - - private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([A-Z])"); - - File resolve(String outputDirectory, String fileName) { - Map replacements = createReplacements(); - String path = outputDirectory; - for (Entry replacement : replacements.entrySet()) { - while (path.contains(replacement.getKey())) { - if (replacement.getValue() == null) { - throw new IllegalStateException("No replacement is available for " - + replacement.getKey()); - } - else { - path = path.replace(replacement.getKey(), replacement.getValue()); - } - } - } - - File outputFile = new File(path, fileName); - if (!outputFile.isAbsolute()) { - outputFile = makeRelativeToConfiguredOutputDir(outputFile); - } - return outputFile; - } - - private Map createReplacements() { - RestDocumentationContext context = RestDocumentationContext.currentContext(); - - Map replacements = new HashMap(); - replacements.put("{methodName}", context == null ? null : context.getTestMethod() - .getName()); - replacements.put("{method-name}", context == null ? null - : camelCaseToDash(context.getTestMethod().getName())); - replacements.put("{method_name}", context == null ? null - : camelCaseToUnderscore(context.getTestMethod().getName())); - replacements.put("{step}", - context == null ? null : Integer.toString(context.getStepCount())); - - return replacements; - } - - private String camelCaseToDash(String string) { - return camelCaseToSeparator(string, "-"); - } - - private String camelCaseToUnderscore(String string) { - return camelCaseToSeparator(string, "_"); - } - - private String camelCaseToSeparator(String string, String separator) { - Matcher matcher = CAMEL_CASE_PATTERN.matcher(string); - StringBuffer result = new StringBuffer(); - while (matcher.find()) { - matcher.appendReplacement(result, separator + matcher.group(1).toLowerCase()); - } - matcher.appendTail(result); - return result.toString(); - } - - private File makeRelativeToConfiguredOutputDir(File outputFile) { - File configuredOutputDir = new DocumentationProperties().getOutputDir(); - if (configuredOutputDir != null) { - return new File(configuredOutputDir, outputFile.getPath()); - } - return null; - } -} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java new file mode 100644 index 00000000..e78b50f6 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java @@ -0,0 +1,89 @@ +/* + * 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.snippet; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.restdocs.config.RestDocumentationContext; +import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; + +/** + * A {@link PlaceholderResolver} that resolves placeholders using a + * {@link RestDocumentationContext}. The following placeholders are supported: + *
    + *
  • {@code step} – the {@link RestDocumentationContext#getStepCount() step current + * count}. + *
  • {@code methodName} - the name of the + * {@link RestDocumentationContext#getTestMethod() current test method} formatted using + * camelCase + *
  • {@code method-name} - the name of the + * {@link RestDocumentationContext#getTestMethod() current test method} formatted using + * kebab-case + *
  • {@code method_name} - the name of the + * {@link RestDocumentationContext#getTestMethod() current test method} formatted using + * snake_case + *
+ * + * @author Andy Wilkinson + */ +public class RestDocumentationContextPlaceholderResolver implements PlaceholderResolver { + + private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([A-Z])"); + + private final RestDocumentationContext context; + + public RestDocumentationContextPlaceholderResolver(RestDocumentationContext context) { + this.context = context; + } + + @Override + public String resolvePlaceholder(String placeholderName) { + if ("step".equals(placeholderName)) { + return Integer.toString(this.context.getStepCount()); + } + if ("methodName".equals(placeholderName)) { + return this.context.getTestMethod().getName(); + } + if ("method-name".equals(placeholderName)) { + return camelCaseToDash(this.context.getTestMethod().getName()); + } + if ("method_name".equals(placeholderName)) { + return camelCaseToUnderscore(this.context.getTestMethod().getName()); + } + return null; + } + + private String camelCaseToDash(String string) { + return camelCaseToSeparator(string, "-"); + } + + private String camelCaseToUnderscore(String string) { + return camelCaseToSeparator(string, "_"); + } + + private String camelCaseToSeparator(String string, String separator) { + Matcher matcher = CAMEL_CASE_PATTERN.matcher(string); + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(result, separator + matcher.group(1).toLowerCase()); + } + matcher.appendTail(result); + return result.toString(); + } + +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java index b81594a5..c75c351e 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java @@ -16,37 +16,32 @@ package org.springframework.restdocs.snippet; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; -import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; -import org.springframework.restdocs.config.RestDocumentationContext; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultHandler; /** * Base class for a {@link ResultHandler} that writes a documentation snippet - * + * * @author Andy Wilkinson */ public abstract class SnippetWritingResultHandler implements ResultHandler { private final Map attributes = new HashMap<>(); - private final String outputDir; + private final String identifier; - private final String fileName; + private final String snippetName; - protected SnippetWritingResultHandler(String outputDir, String fileName, + protected SnippetWritingResultHandler(String identifier, String snippetName, Map attributes) { - this.outputDir = outputDir; - this.fileName = fileName; + this.identifier = identifier; + this.snippetName = snippetName; if (attributes != null) { this.attributes.putAll(attributes); } @@ -57,7 +52,9 @@ public abstract class SnippetWritingResultHandler implements ResultHandler { @Override public void handle(MvcResult result) throws IOException { - try (Writer writer = createWriter()) { + WriterResolver writerResolver = (WriterResolver) result.getRequest() + .getAttribute(WriterResolver.class.getName()); + try (Writer writer = writerResolver.resolve(this.identifier, this.snippetName)) { handle(result, new PrintWriter(writer)); } } @@ -66,26 +63,4 @@ public abstract class SnippetWritingResultHandler implements ResultHandler { return this.attributes; } - private Writer createWriter() throws IOException { - File outputFile = new OutputFileResolver().resolve(this.outputDir, this.fileName - + ".adoc"); - - if (outputFile != null) { - File parent = outputFile.getParentFile(); - if (!parent.isDirectory() && !parent.mkdirs()) { - throw new IllegalStateException("Failed to create directory '" + parent - + "'"); - } - RestDocumentationContext context = RestDocumentationContext.currentContext(); - if (context == null || context.getSnippetEncoding() == null) { - return new FileWriter(outputFile); - } - return new OutputStreamWriter(new FileOutputStream(outputFile), - context.getSnippetEncoding()); - } - else { - return new OutputStreamWriter(System.out); - } - } - } \ No newline at end of file diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java new file mode 100644 index 00000000..fd4a52a7 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java @@ -0,0 +1,94 @@ +/* + * 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.snippet; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import org.springframework.util.PropertyPlaceholderHelper; +import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; + +/** + * Standard implementation of {@link WriterResolver}. + * + * @author Andy Wilkinson + */ +public class StandardWriterResolver implements WriterResolver { + + private String encoding = "UTF-8"; + + private final PlaceholderResolver placeholderResolver; + + private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper( + "{", "}"); + + /** + * Creates a new {@code StandardWriterResolver} that will use the given + * {@code placeholderResolver} to resolve any placeholders in the + * {@code operationName}. + * + * @param placeholderResolver the placeholder resolver + */ + public StandardWriterResolver(PlaceholderResolver placeholderResolver) { + this.placeholderResolver = placeholderResolver; + } + + @Override + public Writer resolve(String operationName, String snippetName) throws IOException { + File outputFile = resolveFile(this.propertyPlaceholderHelper.replacePlaceholders( + operationName, this.placeholderResolver), snippetName + ".adoc"); + + if (outputFile != null) { + createDirectoriesIfNecessary(outputFile); + return new OutputStreamWriter(new FileOutputStream(outputFile), this.encoding); + } + else { + return new OutputStreamWriter(System.out, this.encoding); + } + } + + @Override + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + protected File resolveFile(String outputDirectory, String fileName) { + File outputFile = new File(outputDirectory, fileName); + if (!outputFile.isAbsolute()) { + outputFile = makeRelativeToConfiguredOutputDir(outputFile); + } + return outputFile; + } + + private File makeRelativeToConfiguredOutputDir(File outputFile) { + File configuredOutputDir = new DocumentationProperties().getOutputDir(); + if (configuredOutputDir != null) { + return new File(configuredOutputDir, outputFile.getPath()); + } + return null; + } + + private void createDirectoriesIfNecessary(File outputFile) { + File parent = outputFile.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IllegalStateException("Failed to create directory '" + parent + "'"); + } + } +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java new file mode 100644 index 00000000..45948fc5 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java @@ -0,0 +1,47 @@ +/* + * 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.snippet; + +import java.io.IOException; +import java.io.Writer; + +/** + * A {@code WriterResolver} is used to access the {@link Writer} that should be used to + * write a snippet for an operation that is being documented. + * + * @author Andy Wilkinson + */ +public interface WriterResolver { + + /** + * Returns a writer that can be used to write the snippet with the given name for the + * operation with the given name. + * @param operationName the name of the operation that is being documented + * @param snippetName the name of the snippet + * @return the writer + * @throws IOException if a writer cannot be resolved + */ + Writer resolve(String operationName, String snippetName) throws IOException; + + /** + * Configures the encoding that should be used by any writers produced by this + * resolver + * @param encoding the encoding + */ + void setEncoding(String encoding); + +} diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java index e5aa52a6..95049d78 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java @@ -20,6 +20,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.springframework.restdocs.Attributes.attributes; +import static org.springframework.restdocs.Attributes.key; import static org.springframework.restdocs.RestDocumentation.document; import static org.springframework.restdocs.RestDocumentation.modifyResponseTo; import static org.springframework.restdocs.response.ResponsePostProcessors.maskLinks; @@ -199,6 +201,10 @@ public class RestDocumentationIntegrationTests { assertThat(new File( "build/generated-snippets/custom-snippet-template/curl-request.adoc"), is(snippet().withContents(equalTo("Custom curl request")))); + + mockMvc.perform(get("/")).andDo( + document("index").withCurlRequest( + attributes(key("title").value("Access the index using curl")))); } private void assertExpectedSnippetFilesExist(File directory, String... snippets) { diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java index 35e95c1d..1f684b17 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java @@ -24,6 +24,8 @@ import static org.junit.Assert.assertThat; import java.net.URI; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.springframework.hateoas.mvc.BasicLinkBuilder; import org.springframework.mock.web.MockHttpServletRequest; @@ -41,6 +43,18 @@ public class RestDocumentationConfigurerTests { private MockHttpServletRequest request = new MockHttpServletRequest(); + private RestDocumentationContext context = new RestDocumentationContext(); + + @Before + public void establishContext() { + RestDocumentationContextHolder.setCurrentContext(this.context); + } + + @After + public void clearContext() { + RestDocumentationContextHolder.removeCurrentContext(); + } + @Test public void defaultConfiguration() { RequestPostProcessor postProcessor = new RestDocumentationConfigurer() @@ -118,38 +132,6 @@ public class RestDocumentationConfigurerTests { is(equalTo(Integer.toString(content.length)))); } - @Test - public void defaultSnippetEncodingIsAppliedToTheContext() { - RestDocumentationContext.establishContext(null); - try { - assertThat(RestDocumentationContext.currentContext().getSnippetEncoding(), - is(nullValue())); - new RestDocumentationConfigurer().beforeMockMvcCreated(null, null) - .postProcessRequest(this.request); - assertThat(RestDocumentationContext.currentContext().getSnippetEncoding(), - is(equalTo("UTF-8"))); - } - finally { - RestDocumentationContext.clearContext(); - } - } - - @Test - public void customSnippetEncodingIsAppliedToTheContext() { - RestDocumentationContext.establishContext(null); - try { - assertThat(RestDocumentationContext.currentContext().getSnippetEncoding(), - is(nullValue())); - new RestDocumentationConfigurer().snippets().withEncoding("foo") - .beforeMockMvcCreated(null, null).postProcessRequest(this.request); - assertThat(RestDocumentationContext.currentContext().getSnippetEncoding(), - is(equalTo("foo"))); - } - finally { - RestDocumentationContext.clearContext(); - } - } - private void assertUriConfiguration(String scheme, String host, int port) { assertEquals(scheme, this.request.getScheme()); assertEquals(host, this.request.getServerName()); diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java index 42ab1f40..85490138 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java @@ -24,9 +24,9 @@ import static org.springframework.restdocs.Attributes.attributes; import static org.springframework.restdocs.Attributes.key; import static org.springframework.restdocs.request.RequestDocumentation.documentQueryParameters; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.test.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader; import static org.springframework.restdocs.test.StubMvcResult.result; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import java.io.IOException; @@ -35,6 +35,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.core.io.FileSystemResource; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.restdocs.config.RestDocumentationContext; import org.springframework.restdocs.snippet.SnippetGenerationException; import org.springframework.restdocs.templates.TemplateEngine; import org.springframework.restdocs.templates.TemplateResourceResolver; @@ -43,7 +44,7 @@ import org.springframework.restdocs.test.ExpectedSnippet; /** * Requests for {@link RequestDocumentation} - * + * * @author Andy Wilkinson */ public class RequestDocumentationTests { @@ -107,7 +108,9 @@ public class RequestDocumentationTests { documentQueryParameters("parameter-snippet-request-uri-query-string", null, parameterWithName("a").description("one"), parameterWithName("b").description("two")).handle( - result(get("/?a=alpha&b=bravo"))); + result(get("/?a=alpha&b=bravo").requestAttr( + RestDocumentationContext.class.getName(), + new RestDocumentationContext()))); } @Test diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/OutputFileResolverTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/OutputFileResolverTests.java deleted file mode 100644 index 75755284..00000000 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/OutputFileResolverTests.java +++ /dev/null @@ -1,155 +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.snippet; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.lang.reflect.Method; - -import org.junit.Test; -import org.springframework.restdocs.config.RestDocumentationTestExecutionListener; -import org.springframework.test.context.TestContext; - -/** - * Tests for {@link OutputFileResolver}. - * - * @author Andy Wilkinson - */ -public class OutputFileResolverTests { - - private final OutputFileResolver resolver = new OutputFileResolver(); - - @Test - public void noConfiguredOutputDirectoryAndRelativeInput() { - assertThat(this.resolver.resolve("foo", "bar.txt"), is(nullValue())); - } - - @Test - public void absoluteInput() { - String absolutePath = new File("foo").getAbsolutePath(); - assertThat(this.resolver.resolve(absolutePath, "bar.txt"), is(new File( - absolutePath, "bar.txt"))); - } - - @Test - public void configuredOutputAndRelativeInput() { - String outputDir = new File("foo").getAbsolutePath(); - System.setProperty("org.springframework.restdocs.outputDir", outputDir); - try { - assertThat(this.resolver.resolve("bar", "baz.txt"), is(new File(outputDir, - "bar/baz.txt"))); - } - finally { - System.clearProperty("org.springframework.restdocs.outputDir"); - } - } - - @Test - public void configuredOutputAndAbsoluteInput() { - String outputDir = new File("foo").getAbsolutePath(); - String absolutePath = new File("bar").getAbsolutePath(); - System.setProperty("org.springframework.restdocs.outputDir", outputDir); - try { - assertThat(this.resolver.resolve(absolutePath, "baz.txt"), is(new File( - absolutePath, "baz.txt"))); - } - finally { - System.clearProperty("org.springframework.restdocs.outputDir"); - } - } - - @Test(expected = IllegalStateException.class) - public void placeholderWithoutAReplacement() { - this.resolver.resolve("{method-name}", "foo.txt"); - } - - @Test - public void dashSeparatedMethodName() throws Exception { - RestDocumentationTestExecutionListener listener = new RestDocumentationTestExecutionListener(); - TestContext testContext = mock(TestContext.class); - Method method = getClass().getMethod("dashSeparatedMethodName"); - when(testContext.getTestMethod()).thenReturn(method); - listener.beforeTestMethod(testContext); - try { - assertThat(this.resolver.resolve(new File("{method-name}").getAbsolutePath(), - "foo.txt"), - is(new File(new File("dash-separated-method-name").getAbsolutePath(), - "foo.txt"))); - } - finally { - listener.afterTestMethod(testContext); - } - } - - @Test - public void underscoreSeparatedMethodName() throws Exception { - RestDocumentationTestExecutionListener listener = new RestDocumentationTestExecutionListener(); - TestContext testContext = mock(TestContext.class); - Method method = getClass().getMethod("underscoreSeparatedMethodName"); - when(testContext.getTestMethod()).thenReturn(method); - listener.beforeTestMethod(testContext); - try { - assertThat( - this.resolver.resolve(new File("{method_name}").getAbsolutePath(), - "foo.txt"), - is(new File(new File("underscore_separated_method_name") - .getAbsolutePath(), "foo.txt"))); - } - finally { - listener.afterTestMethod(testContext); - } - } - - @Test - public void camelCaseMethodName() throws Exception { - RestDocumentationTestExecutionListener listener = new RestDocumentationTestExecutionListener(); - TestContext testContext = mock(TestContext.class); - Method method = getClass().getMethod("camelCaseMethodName"); - when(testContext.getTestMethod()).thenReturn(method); - listener.beforeTestMethod(testContext); - try { - assertThat(this.resolver.resolve(new File("{methodName}").getAbsolutePath(), - "foo.txt"), - is(new File(new File("camelCaseMethodName").getAbsolutePath(), - "foo.txt"))); - } - finally { - listener.afterTestMethod(testContext); - } - } - - @Test - public void stepCount() throws Exception { - RestDocumentationTestExecutionListener listener = new RestDocumentationTestExecutionListener(); - TestContext testContext = mock(TestContext.class); - Method method = getClass().getMethod("stepCount"); - when(testContext.getTestMethod()).thenReturn(method); - listener.beforeTestMethod(testContext); - try { - assertThat(this.resolver.resolve(new File("{step}").getAbsolutePath(), - "foo.txt"), is(new File(new File("0").getAbsolutePath(), "foo.txt"))); - } - finally { - listener.afterTestMethod(testContext); - } - } -} diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java new file mode 100644 index 00000000..468b016b --- /dev/null +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java @@ -0,0 +1,60 @@ +package org.springframework.restdocs.snippet; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; + +import org.junit.Test; +import org.springframework.restdocs.config.RestDocumentationContext; +import org.springframework.test.context.TestContext; +import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; + +/** + * Tests for {@link RestDocumentationContextPlaceholderResolver} + * + * @author Andy Wilkinson + * + */ +public class RestDocumentationContextPlaceholderResolverTests { + + private final TestContext testContext = mock(TestContext.class); + + private final RestDocumentationContext context = new RestDocumentationContext( + this.testContext); + + private final PlaceholderResolver resolver = new RestDocumentationContextPlaceholderResolver( + this.context); + + @Test + public void dashSeparatedMethodName() throws Exception { + when(this.testContext.getTestMethod()).thenReturn( + getClass().getMethod("dashSeparatedMethodName")); + assertThat(this.resolver.resolvePlaceholder("method-name"), + equalTo("dash-separated-method-name")); + } + + @Test + public void underscoreSeparatedMethodName() throws Exception { + when(this.testContext.getTestMethod()).thenReturn( + getClass().getMethod("underscoreSeparatedMethodName")); + assertThat(this.resolver.resolvePlaceholder("method_name"), + equalTo("underscore_separated_method_name")); + } + + @Test + public void camelCaseMethodName() throws Exception { + Method method = getClass().getMethod("camelCaseMethodName"); + when(this.testContext.getTestMethod()).thenReturn(method); + assertThat(this.resolver.resolvePlaceholder("methodName"), + equalTo("camelCaseMethodName")); + } + + @Test + public void stepCount() throws Exception { + assertThat(this.resolver.resolvePlaceholder("step"), equalTo("0")); + } + +} diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java new file mode 100644 index 00000000..37d8fa44 --- /dev/null +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java @@ -0,0 +1,80 @@ +/* + * 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.snippet; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +import java.io.File; + +import org.junit.Test; +import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; + +/** + * Tests for {@link StandardWriterResolver}. + * + * @author Andy Wilkinson + */ +public class StandardWriterResolverTests { + + private final PlaceholderResolver placeholderResolver = mock(PlaceholderResolver.class); + + private final StandardWriterResolver resolver = new StandardWriterResolver( + this.placeholderResolver); + + @Test + public void noConfiguredOutputDirectoryAndRelativeInput() { + assertThat(this.resolver.resolveFile("foo", "bar.txt"), is(nullValue())); + } + + @Test + public void absoluteInput() { + String absolutePath = new File("foo").getAbsolutePath(); + assertThat(this.resolver.resolveFile(absolutePath, "bar.txt"), is(new File( + absolutePath, "bar.txt"))); + } + + @Test + public void configuredOutputAndRelativeInput() { + String outputDir = new File("foo").getAbsolutePath(); + System.setProperty("org.springframework.restdocs.outputDir", outputDir); + try { + assertThat(this.resolver.resolveFile("bar", "baz.txt"), is(new File( + outputDir, "bar/baz.txt"))); + } + finally { + System.clearProperty("org.springframework.restdocs.outputDir"); + } + } + + @Test + public void configuredOutputAndAbsoluteInput() { + String outputDir = new File("foo").getAbsolutePath(); + String absolutePath = new File("bar").getAbsolutePath(); + System.setProperty("org.springframework.restdocs.outputDir", outputDir); + try { + assertThat(this.resolver.resolveFile(absolutePath, "baz.txt"), is(new File( + absolutePath, "baz.txt"))); + } + finally { + System.clearProperty("org.springframework.restdocs.outputDir"); + } + } + +} diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/test/RestDocumentationRequestBuilders.java b/spring-restdocs/src/test/java/org/springframework/restdocs/test/RestDocumentationRequestBuilders.java new file mode 100644 index 00000000..05bcd7b4 --- /dev/null +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/test/RestDocumentationRequestBuilders.java @@ -0,0 +1,18 @@ +package org.springframework.restdocs.test; + +import org.springframework.restdocs.config.RestDocumentationContext; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +public class RestDocumentationRequestBuilders { + + private RestDocumentationRequestBuilders() { + + } + + public static MockHttpServletRequestBuilder get(String urlTemplate) { + return MockMvcRequestBuilders.get(urlTemplate).requestAttr( + RestDocumentationContext.class.getName(), new RestDocumentationContext()); + } + +} diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/test/StubMvcResult.java b/spring-restdocs/src/test/java/org/springframework/restdocs/test/StubMvcResult.java index 7b1d3150..441398a2 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/test/StubMvcResult.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/test/StubMvcResult.java @@ -19,6 +19,10 @@ package org.springframework.restdocs.test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; +import org.springframework.restdocs.config.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; @@ -30,7 +34,7 @@ import org.springframework.web.servlet.ModelAndView; /** * A minimal stub implementation of {@link MvcResult} - * + * * @author Andy Wilkinson * */ @@ -88,6 +92,13 @@ public class StubMvcResult implements MvcResult { this.request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(new StandardTemplateResourceResolver())); } + RestDocumentationContext context = new RestDocumentationContext(); + this.request.setAttribute(RestDocumentationContext.class.getName(), context); + if (this.request.getAttribute(WriterResolver.class.getName()) == null) { + this.request.setAttribute(WriterResolver.class.getName(), + new StandardWriterResolver( + new RestDocumentationContextPlaceholderResolver(context))); + } this.response = response; }