From c3a9bdfa9461ec11e325d1e65838df7ded85c516 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 30 Jun 2015 15:28:22 +0100 Subject: [PATCH] Allow users to control the writer that is used to produce each snippet Previously, SnippetWritingResultHandler hard-coded its own logic for creating the Writer that used to write its snippet. This made it impossible to take complete control over the output location, filename suffix, etc. This commit introduces a new interface, WriterResolver, and a default implementation, StandardWriterResolver. It provides the same functionality as before, but SnippetWritingResultHandler now retrieves an instance of WriterResolver from the request's attributes. This allows users to provide their own WriterResolver implementation. Closes gh-100 --- .../restdocs/ResponseModifier.java | 10 +- .../restdocs/RestDocumentation.java | 8 +- .../RestDocumentationResultHandler.java | 28 ++-- .../config/RestDocumentationConfigurer.java | 49 +++++- .../config/RestDocumentationContext.java | 54 ++---- .../RestDocumentationContextHolder.java | 42 +++++ ...estDocumentationTestExecutionListener.java | 5 +- .../restdocs/config/SnippetConfigurer.java | 8 +- .../restdocs/curl/CurlDocumentation.java | 10 +- .../restdocs/http/HttpDocumentation.java | 20 +-- .../hypermedia/HypermediaDocumentation.java | 6 +- .../hypermedia/LinkSnippetResultHandler.java | 4 +- .../payload/FieldSnippetResultHandler.java | 4 +- .../payload/PayloadDocumentation.java | 12 +- .../RequestFieldSnippetResultHandler.java | 4 +- .../ResponseFieldSnippetResultHandler.java | 4 +- .../QueryParametersSnippetResultHandler.java | 4 +- .../request/RequestDocumentation.java | 7 +- .../restdocs/snippet/OutputFileResolver.java | 101 ------------ ...cumentationContextPlaceholderResolver.java | 89 ++++++++++ .../snippet/SnippetWritingResultHandler.java | 43 +---- .../snippet/StandardWriterResolver.java | 94 +++++++++++ .../restdocs/snippet/WriterResolver.java | 47 ++++++ .../RestDocumentationIntegrationTests.java | 6 + .../RestDocumentationConfigurerTests.java | 46 ++---- .../request/RequestDocumentationTests.java | 9 +- .../snippet/OutputFileResolverTests.java | 155 ------------------ ...tationContextPlaceholderResolverTests.java | 60 +++++++ .../snippet/StandardWriterResolverTests.java | 80 +++++++++ .../RestDocumentationRequestBuilders.java | 18 ++ .../restdocs/test/StubMvcResult.java | 13 +- 31 files changed, 597 insertions(+), 443 deletions(-) create mode 100644 spring-restdocs/src/main/java/org/springframework/restdocs/config/RestDocumentationContextHolder.java delete mode 100644 spring-restdocs/src/main/java/org/springframework/restdocs/snippet/OutputFileResolver.java create mode 100644 spring-restdocs/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java create mode 100644 spring-restdocs/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java create mode 100644 spring-restdocs/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java delete mode 100644 spring-restdocs/src/test/java/org/springframework/restdocs/snippet/OutputFileResolverTests.java create mode 100644 spring-restdocs/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java create mode 100644 spring-restdocs/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java create mode 100644 spring-restdocs/src/test/java/org/springframework/restdocs/test/RestDocumentationRequestBuilders.java 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; }