Add AsciidoctorJ 1.6 and 2.0 compatibility
AsciidoctorJ 1.6 is not backwards compatible with AsciidoctorJ 1.5 and AsciidoctorJ 2.0 is not backwards compatible with AsciidoctorJ 1.6 or 1.5. The backwards incompatibilities are such that they cannot be worked around using reflection so code needs to be compiled against each of the three versions. To this end, this commit splits spring-restdocs-asciidoctor into several separate modules: 1. spring-restdocs-asciidoctor-support 2. spring-restdocs-asciidoctor-1.5 3. spring-restdocs-asciidoctor-1.6 4. spring-restdocs-asciidoctor-2.0 spring-restdocs-asciidoctor-support contains support code that is not tied to a specific version of AsciidoctorJ and can be used with 1.5, 1.6 and 2.0. The other three modules contain code that is specific to a particular version of AsciidoctorJ. Each version-specific module uses class names that are unique across all three modules and is written in such a way that they will back off when used in an environment with a different version of AsciidoctorJ. The existing spring-restdocs-asciidoctor module has been modified to merge the version specific jars and the support jar together into a single jar that supports AsciidoctorJ 1.5, 1.6, and 2.0. The above-described changes should allow users to depend upon spring-restdocs-asciidoctor as before and to now be able to use AsciidoctorJ 1.5, 1.6, or 2.0. Closes gh-581
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.asciidoctor;
|
||||
|
||||
import org.asciidoctor.ast.Document;
|
||||
import org.asciidoctor.extension.Preprocessor;
|
||||
import org.asciidoctor.extension.PreprocessorReader;
|
||||
|
||||
/**
|
||||
* {@link Preprocessor} that sets defaults for REST Docs-related {@link Document}
|
||||
* attributes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
final class DefaultAttributesPreprocessor extends Preprocessor {
|
||||
|
||||
private final SnippetsDirectoryResolver snippetsDirectoryResolver = new SnippetsDirectoryResolver();
|
||||
|
||||
@Override
|
||||
public PreprocessorReader process(Document document, PreprocessorReader reader) {
|
||||
document.setAttr("snippets", this.snippetsDirectoryResolver
|
||||
.getSnippetsDirectory(document.getAttributes()), false);
|
||||
return reader;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.asciidoctor;
|
||||
|
||||
import org.asciidoctor.Asciidoctor;
|
||||
import org.asciidoctor.extension.spi.ExtensionRegistry;
|
||||
|
||||
/**
|
||||
* Asciidoctor {@link ExtensionRegistry} for Spring REST Docs.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public final class RestDocsExtensionRegistry implements ExtensionRegistry {
|
||||
|
||||
@Override
|
||||
public void register(Asciidoctor asciidoctor) {
|
||||
asciidoctor.javaExtensionRegistry()
|
||||
.preprocessor(new DefaultAttributesPreprocessor());
|
||||
asciidoctor.rubyExtensionRegistry()
|
||||
.loadClass(RestDocsExtensionRegistry.class
|
||||
.getResourceAsStream("/extensions/operation_block_macro.rb"))
|
||||
.blockMacro("operation", "OperationBlockMacro");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.asciidoctor;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Resolves the directory from which snippets can be read for inclusion in an Asciidoctor
|
||||
* document. The resolved directory is relative to the {@code docdir} of the Asciidoctor
|
||||
* document that it being rendered.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SnippetsDirectoryResolver {
|
||||
|
||||
File getSnippetsDirectory(Map<String, Object> attributes) {
|
||||
if (System.getProperty("maven.home") != null) {
|
||||
return getMavenSnippetsDirectory(attributes);
|
||||
}
|
||||
return getGradleSnippetsDirectory(attributes);
|
||||
}
|
||||
|
||||
private File getMavenSnippetsDirectory(Map<String, Object> attributes) {
|
||||
Path docdir = Paths.get(getRequiredAttribute(attributes, "docdir"));
|
||||
return new File(docdir.relativize(findPom(docdir).getParent()).toFile(),
|
||||
"target/generated-snippets");
|
||||
}
|
||||
|
||||
private Path findPom(Path docdir) {
|
||||
Path path = docdir;
|
||||
while (path != null) {
|
||||
Path pom = path.resolve("pom.xml");
|
||||
if (Files.isRegularFile(pom)) {
|
||||
return pom;
|
||||
}
|
||||
path = path.getParent();
|
||||
}
|
||||
throw new IllegalStateException("pom.xml not found in '" + docdir + "' or above");
|
||||
}
|
||||
|
||||
private File getGradleSnippetsDirectory(Map<String, Object> attributes) {
|
||||
return new File(
|
||||
getRequiredAttribute(attributes, "gradle-projectdir",
|
||||
() -> getRequiredAttribute(attributes, "projectdir")),
|
||||
"build/generated-snippets");
|
||||
}
|
||||
|
||||
private String getRequiredAttribute(Map<String, Object> attributes, String name) {
|
||||
return getRequiredAttribute(attributes, name, null);
|
||||
}
|
||||
|
||||
private String getRequiredAttribute(Map<String, Object> attributes, String name,
|
||||
Supplier<String> fallback) {
|
||||
String attribute = (String) attributes.get(name);
|
||||
if (attribute == null || attribute.length() == 0) {
|
||||
if (fallback != null) {
|
||||
return fallback.get();
|
||||
}
|
||||
throw new IllegalStateException(name + " attribute not found");
|
||||
}
|
||||
return attribute;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spring REST Docs Asciidoctor extensions.
|
||||
*/
|
||||
package org.springframework.restdocs.asciidoctor;
|
||||
@@ -1 +1,2 @@
|
||||
org.springframework.restdocs.asciidoctor.RestDocsExtensionRegistry
|
||||
org.springframework.restdocs.asciidoctor.RestDocsAsciidoctorJ15ExtensionRegistry
|
||||
org.springframework.restdocs.asciidoctor.RestDocsAsciidoctorJ16ExtensionRegistry
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.restdocs.asciidoctor.RestDocsAsciidoctorJ20ExtensionRegistry
|
||||
@@ -1,145 +0,0 @@
|
||||
require 'asciidoctor/extensions'
|
||||
require 'stringio'
|
||||
|
||||
# Spring REST Docs block macro to import multiple snippet of an operation at
|
||||
# once
|
||||
#
|
||||
# Usage
|
||||
#
|
||||
# operation::operation-name[snippets='snippet-name1,snippet-name2']
|
||||
#
|
||||
class OperationBlockMacro < Asciidoctor::Extensions::BlockMacroProcessor
|
||||
use_dsl
|
||||
named :operation
|
||||
|
||||
def process(parent, operation, attributes)
|
||||
snippets_dir = parent.document.attributes['snippets'].to_s
|
||||
snippet_names = attributes.fetch 'snippets', ''
|
||||
operation = parent.sub_attributes operation
|
||||
snippet_titles = SnippetTitles.new parent.document.attributes
|
||||
content = read_snippets(snippets_dir, snippet_names, parent, operation,
|
||||
snippet_titles)
|
||||
add_blocks(content, parent.document, parent) unless content.empty?
|
||||
nil
|
||||
end
|
||||
|
||||
def read_snippets(snippets_dir, snippet_names, parent, operation,
|
||||
snippet_titles)
|
||||
snippets = snippets_to_include(snippet_names, snippets_dir, operation)
|
||||
if snippets.empty?
|
||||
warn "No snippets were found for operation #{operation} in"\
|
||||
"#{snippets_dir}"
|
||||
"No snippets found for operation::#{operation}"
|
||||
else
|
||||
do_read_snippets(snippets, parent, operation, snippet_titles)
|
||||
end
|
||||
end
|
||||
|
||||
def do_read_snippets(snippets, parent, operation, snippet_titles)
|
||||
content = StringIO.new
|
||||
section_level = parent.level + 1
|
||||
section_id = parent.id
|
||||
snippets.each do |snippet|
|
||||
append_snippet_block(content, snippet, section_level, section_id,
|
||||
operation, snippet_titles)
|
||||
end
|
||||
content.string
|
||||
end
|
||||
|
||||
def add_blocks(content, doc, parent)
|
||||
options = { safe: doc.options[:safe],
|
||||
attributes: { 'projectdir' => doc.attr(:projectdir) } }
|
||||
fragment = Asciidoctor.load content, options
|
||||
fragment.blocks.each do |b|
|
||||
b.parent = parent
|
||||
parent << b
|
||||
end
|
||||
parent.find_by.each do |b|
|
||||
b.parent = b.parent unless b.is_a? Asciidoctor::Document
|
||||
end
|
||||
end
|
||||
|
||||
def snippets_to_include(snippet_names, snippets_dir, operation)
|
||||
if snippet_names.empty?
|
||||
all_snippets snippets_dir, operation
|
||||
else
|
||||
snippet_names.split(',').map do |name|
|
||||
path = File.join snippets_dir, operation, "#{name}.adoc"
|
||||
Snippet.new path, name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def all_snippets(snippets_dir, operation)
|
||||
operation_dir = File.join snippets_dir, operation
|
||||
return [] unless Dir.exist? operation_dir
|
||||
Dir.entries(operation_dir)
|
||||
.sort
|
||||
.select { |file| file.end_with? '.adoc' }
|
||||
.map { |file| Snippet.new(File.join(operation_dir, file), file[0..-6]) }
|
||||
end
|
||||
|
||||
def append_snippet_block(content, snippet, section_level, section_id,
|
||||
operation, snippet_titles)
|
||||
write_title content, snippet, section_level, section_id, snippet_titles
|
||||
write_content content, snippet, operation
|
||||
end
|
||||
|
||||
def write_content(content, snippet, operation)
|
||||
if File.file? snippet.path
|
||||
content.puts File.readlines(snippet.path).join
|
||||
else
|
||||
warn "Snippet #{snippet.name} not found at #{snippet.path} for"\
|
||||
" operation #{operation}"
|
||||
content.puts "Snippet #{snippet.name} not found for"\
|
||||
" operation::#{operation}"
|
||||
content.puts ''
|
||||
end
|
||||
end
|
||||
|
||||
def write_title(content, snippet, level, id, snippet_titles)
|
||||
section_level = '=' * (level + 1)
|
||||
title = snippet_titles.title_for_snippet snippet
|
||||
content.puts "[[#{id}_#{snippet.name.sub '-', '_'}]]"
|
||||
content.puts "#{section_level} #{title}"
|
||||
content.puts ''
|
||||
end
|
||||
|
||||
# Details of a snippet to be rendered
|
||||
class Snippet
|
||||
attr_reader :name, :path
|
||||
|
||||
def initialize(path, name)
|
||||
@path = path
|
||||
@name = name
|
||||
@snippet_titles
|
||||
end
|
||||
end
|
||||
|
||||
class SnippetTitles
|
||||
@defaults = { 'http-request' => 'HTTP request',
|
||||
'curl-request' => 'Curl request',
|
||||
'httpie-request' => 'HTTPie request',
|
||||
'request-body' => 'Request body',
|
||||
'request-fields' => 'Request fields',
|
||||
'http-response' => 'HTTP response',
|
||||
'response-body' => 'Response body',
|
||||
'response-fields' => 'Response fields',
|
||||
'links' => 'Links' }
|
||||
|
||||
class << self
|
||||
attr_reader :defaults
|
||||
end
|
||||
|
||||
def initialize(document_attributes)
|
||||
@document_attributes = document_attributes
|
||||
end
|
||||
|
||||
def title_for_snippet(snippet)
|
||||
attribute_name = "operation-#{snippet.name}-title"
|
||||
@document_attributes.fetch attribute_name do
|
||||
SnippetTitles.defaults.fetch snippet.name, snippet.name.sub('-', ' ').capitalize
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.asciidoctor;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.asciidoctor.Asciidoctor;
|
||||
import org.asciidoctor.Attributes;
|
||||
import org.asciidoctor.Options;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultAttributesPreprocessor}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class DefaultAttributesPreprocessorTests {
|
||||
|
||||
@Test
|
||||
public void snippetsAttributeIsSet() {
|
||||
Options options = new Options();
|
||||
options.setAttributes(new Attributes("projectdir=../../.."));
|
||||
String converted = Asciidoctor.Factory.create().convert("{snippets}", options);
|
||||
assertThat(converted)
|
||||
.contains("build" + File.separatorChar + "generated-snippets");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snippetsAttributeFromConvertArgumentIsNotOverridden() {
|
||||
Options options = new Options();
|
||||
options.setAttributes(new Attributes("snippets=custom projectdir=../../.."));
|
||||
String converted = Asciidoctor.Factory.create().convert("{snippets}", options);
|
||||
assertThat(converted).contains("custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snippetsAttributeFromDocumentPreambleIsNotOverridden() {
|
||||
Options options = new Options();
|
||||
options.setAttributes(new Attributes("projectdir=../../.."));
|
||||
String converted = Asciidoctor.Factory.create()
|
||||
.convert(":snippets: custom\n{snippets}", options);
|
||||
assertThat(converted).contains("custom");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.asciidoctor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
/**
|
||||
* Tests for {@link SnippetsDirectoryResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class SnippetsDirectoryResolverTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void mavenProjectsUseTargetGeneratedSnippetsRelativeToDocdir()
|
||||
throws IOException {
|
||||
this.temporaryFolder.newFile("pom.xml");
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("docdir",
|
||||
new File(this.temporaryFolder.getRoot(), "src/main/asciidoc")
|
||||
.getAbsolutePath());
|
||||
File snippetsDirectory = getMavenSnippetsDirectory(attributes);
|
||||
assertThat(snippetsDirectory).isRelative();
|
||||
assertThat(snippetsDirectory)
|
||||
.isEqualTo(new File("../../../target/generated-snippets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalStateExceptionWhenMavenPomCannotBeFound() throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
String docdir = new File(this.temporaryFolder.getRoot(), "src/main/asciidoc")
|
||||
.getAbsolutePath();
|
||||
attributes.put("docdir", docdir);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("pom.xml not found in '" + docdir + "' or above"));
|
||||
getMavenSnippetsDirectory(attributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalStateWhenDocdirAttributeIsNotSetInMavenProject()
|
||||
throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage(equalTo("docdir attribute not found"));
|
||||
getMavenSnippetsDirectory(attributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gradleProjectsUseBuildGeneratedSnippetsBeneathGradleProjectdir()
|
||||
throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("gradle-projectdir", "project/dir");
|
||||
File snippetsDirectory = new SnippetsDirectoryResolver()
|
||||
.getSnippetsDirectory(attributes);
|
||||
assertThat(snippetsDirectory)
|
||||
.isEqualTo(new File("project/dir/build/generated-snippets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gradleProjectsUseBuildGeneratedSnippetsBeneathGradleProjectdirWhenBothItAndProjectdirAreSet()
|
||||
throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("gradle-projectdir", "project/dir");
|
||||
attributes.put("projectdir", "fallback/dir");
|
||||
File snippetsDirectory = new SnippetsDirectoryResolver()
|
||||
.getSnippetsDirectory(attributes);
|
||||
assertThat(snippetsDirectory)
|
||||
.isEqualTo(new File("project/dir/build/generated-snippets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gradleProjectsUseBuildGeneratedSnippetsBeneathProjectdirWhenGradleProjectdirIsNotSet()
|
||||
throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("projectdir", "project/dir");
|
||||
File snippetsDirectory = new SnippetsDirectoryResolver()
|
||||
.getSnippetsDirectory(attributes);
|
||||
assertThat(snippetsDirectory)
|
||||
.isEqualTo(new File("project/dir/build/generated-snippets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalStateWhenGradleProjectdirAndProjectdirAttributesAreNotSetInGradleProject()
|
||||
throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage(equalTo("projectdir attribute not found"));
|
||||
new SnippetsDirectoryResolver().getSnippetsDirectory(attributes);
|
||||
}
|
||||
|
||||
private File getMavenSnippetsDirectory(Map<String, Object> attributes) {
|
||||
System.setProperty("maven.home", "/maven/home");
|
||||
try {
|
||||
return new SnippetsDirectoryResolver().getSnippetsDirectory(attributes);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("maven.home");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user