diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index c68b9d43..fb9977c8 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -627,10 +627,14 @@ generated snippets. templates] are provided for each of the snippets that Spring REST Docs can produce. To customize a snippet's content, you can provide your own template. -Templates are loaded from the classpath in the `org.springframework.restdocs.templates` -package and each template is named after the snippet that it will produce. For example, to +Templates are loaded from the classpath from an `org.springframework.restdocs.templates` +subpackage. The name of the subpackage is determined by the ID of the template format +that is in use. The default template format, Asciidoctor, has the ID `asciidoctor` so +snippets are loaded from `org.springframework.restdocs.templates.asciidoctor`. Each +template is named after the snippet that it will produce. For example, to override the template for the `curl-request.adoc` snippet, create a template named -`curl-request.snippet` in `src/test/resources/org/springframework/restdocs/templates`. +`curl-request.snippet` in +`src/test/resources/org/springframework/restdocs/templates/asciidoctor`. diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java index 22912eb3..7e0b38ac 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java @@ -22,12 +22,20 @@ import org.springframework.core.io.Resource; /** * Standard implementation of {@link TemplateResourceResolver}. *

- * Templates are resolved by looking for a resource on the classpath named - * {@code org/springframework/restdocs/templates/{name}.snippet}. If no such - * resource exists an attempt is made to return a default resource that is appropriate for - * the configured snippet format. + * Templates are resolved by looking for resources on the classpath. The following + * locations are checked in order: + *

    + *
  1. + * org/springframework/restdocs/templates/${templateFormatId}/${name}.snippet + *
  2. + *
  3. org/springframework/restdocs/templates/${name}.snippet
  4. + *
  5. + * org/springframework/restdocs/templates/${templateFormatId}/default-${name}.snippet + *
  6. + *
* * @author Andy Wilkinson + * @see TemplateFormat#getId() */ public class StandardTemplateResourceResolver implements TemplateResourceResolver { @@ -57,18 +65,37 @@ public class StandardTemplateResourceResolver implements TemplateResourceResolve @Override public Resource resolveTemplateResource(String name) { - ClassPathResource classPathResource = new ClassPathResource( - "org/springframework/restdocs/templates/" + name + ".snippet"); - if (!classPathResource.exists()) { - classPathResource = new ClassPathResource( - "org/springframework/restdocs/templates/" - + this.templateFormat.getId() + "/" + name + ".snippet"); - if (!classPathResource.exists()) { - throw new IllegalStateException("Template named '" + name - + "' could not be resolved"); - } + Resource formatSpecificCustomTemplate = getFormatSpecificCustomTemplate(name); + if (formatSpecificCustomTemplate.exists()) { + return formatSpecificCustomTemplate; } - return classPathResource; + Resource customTemplate = getCustomTemplate(name); + if (customTemplate.exists()) { + return customTemplate; + } + Resource defaultTemplate = getDefaultTemplate(name); + if (defaultTemplate.exists()) { + return defaultTemplate; + } + throw new IllegalStateException("Template named '" + name + + "' could not be resolved"); + } + + private Resource getFormatSpecificCustomTemplate(String name) { + return new ClassPathResource(String.format( + "org/springframework/restdocs/templates/%s/%s.snippet", + this.templateFormat.getId(), name)); + } + + private Resource getCustomTemplate(String name) { + return new ClassPathResource(String.format( + "org/springframework/restdocs/templates/%s.snippet", name)); + } + + private Resource getDefaultTemplate(String name) { + return new ClassPathResource(String.format( + "org/springframework/restdocs/templates/%s/default-%s.snippet", + this.templateFormat.getId(), name)); } } diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/curl-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-curl-request.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/curl-request.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-curl-request.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/http-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-request.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/http-request.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-request.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/http-response.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-response.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/http-response.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-response.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/links.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-links.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/links.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-links.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/path-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-path-parameters.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/path-parameters.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-path-parameters.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-fields.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/request-fields.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-fields.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/request-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-headers.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/request-headers.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-headers.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/request-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parameters.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/request-parameters.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parameters.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-fields.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/response-fields.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-fields.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/response-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-headers.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/response-headers.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-headers.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/curl-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-curl-request.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/curl-request.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-curl-request.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/http-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-request.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/http-request.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-request.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/http-response.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-response.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/http-response.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-response.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/links.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-links.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/links.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-links.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/path-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-path-parameters.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/path-parameters.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-path-parameters.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/request-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-fields.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/request-fields.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-fields.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/request-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-headers.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/request-headers.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-headers.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/request-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parameters.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/request-parameters.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parameters.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/response-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-fields.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/response-fields.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-fields.snippet diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/response-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-headers.snippet similarity index 100% rename from spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/response-headers.snippet rename to spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-headers.snippet diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java index 8d8ef578..1d152335 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java @@ -47,29 +47,17 @@ public class StandardTemplateResourceResolverTests { private final TestClassLoader classLoader = new TestClassLoader(); @Test - public void customSnippetResolution() throws Exception { - this.classLoader.addResource( - "org/springframework/restdocs/templates/test.snippet", getClass() - .getResource("test.snippet")); - Resource snippet = doWithThreadContextClassLoader(this.classLoader, - new Callable() { - - @Override - public Resource call() { - return StandardTemplateResourceResolverTests.this.resolver - .resolveTemplateResource("test"); - } - - }); - - assertThat(snippet.getURL(), is(equalTo(getClass().getResource("test.snippet")))); - } - - @Test - public void fallsBackToDefaultSnippet() throws Exception { + public void formatSpecificCustomSnippetHasHighestPrecedence() throws Exception { this.classLoader.addResource( "org/springframework/restdocs/templates/asciidoctor/test.snippet", - getClass().getResource("test.snippet")); + getClass().getResource("test-format-specific-custom.snippet")); + this.classLoader.addResource( + "org/springframework/restdocs/templates/test.snippet", getClass() + .getResource("test-custom.snippet")); + this.classLoader + .addResource( + "org/springframework/restdocs/templates/asciidoctor/default-test.snippet", + getClass().getResource("test-default.snippet")); Resource snippet = doWithThreadContextClassLoader(this.classLoader, new Callable() { @@ -81,11 +69,59 @@ public class StandardTemplateResourceResolverTests { }); - assertThat(snippet.getURL(), is(equalTo(getClass().getResource("test.snippet")))); + assertThat( + snippet.getURL(), + is(equalTo(getClass().getResource("test-format-specific-custom.snippet")))); } @Test - public void failsIfCustomAndDefaultSnippetDoNotExist() throws Exception { + public void generalCustomSnippetIsUsedInAbsenceOfFormatSpecificCustomSnippet() + throws Exception { + this.classLoader.addResource( + "org/springframework/restdocs/templates/test.snippet", getClass() + .getResource("test-custom.snippet")); + this.classLoader + .addResource( + "org/springframework/restdocs/templates/asciidoctor/default-test.snippet", + getClass().getResource("test-default.snippet")); + Resource snippet = doWithThreadContextClassLoader(this.classLoader, + new Callable() { + + @Override + public Resource call() { + return StandardTemplateResourceResolverTests.this.resolver + .resolveTemplateResource("test"); + } + + }); + + assertThat(snippet.getURL(), + is(equalTo(getClass().getResource("test-custom.snippet")))); + } + + @Test + public void defaultSnippetIsUsedInAbsenceOfCustomSnippets() throws Exception { + this.classLoader + .addResource( + "org/springframework/restdocs/templates/asciidoctor/default-test.snippet", + getClass().getResource("test-default.snippet")); + Resource snippet = doWithThreadContextClassLoader(this.classLoader, + new Callable() { + + @Override + public Resource call() { + return StandardTemplateResourceResolverTests.this.resolver + .resolveTemplateResource("test"); + } + + }); + + assertThat(snippet.getURL(), + is(equalTo(getClass().getResource("test-default.snippet")))); + } + + @Test + public void failsIfCustomAndDefaultSnippetsDoNotExist() throws Exception { this.thrown.expect(IllegalStateException.class); this.thrown.expectMessage(equalTo("Template named 'test' could not be resolved")); doWithThreadContextClassLoader(this.classLoader, new Callable() { diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-custom.snippet similarity index 100% rename from spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test.snippet rename to spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-custom.snippet diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-default.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-default.snippet new file mode 100644 index 00000000..e69de29b diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-format-specific-custom.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-format-specific-custom.snippet new file mode 100644 index 00000000..e69de29b