sg-ad / swagger2markup

A Swagger2Markup (AsciiDoc/Markdown) converter to simplify the generation of an up-to-date RESTful API documentation by combining documentation that’s been hand-written with auto-generated API documentation.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Swagger2Markup

Build Status Coverage Status download Apache License 2 Twitter Join%20Chat

Overview

The primary goal of this project is to simplify the generation of an up-to-date RESTful API documentation by combining documentation that’s been hand-written with auto-generated API documentation produced by Swagger. The result is intended to be an up-to-date, easy-to-read, on- and offline user guide, comparable to GitHub’s API documentation. The output of Swagger2Markup can be used as an alternative to swagger-ui and can be served as static content.

Swagger2Markup converts a Swagger JSON or YAML file into several AsciiDoc or GitHub Flavored Markdown documents which can be combined with hand-written documentation. The Swagger source file can be located locally or remotely via HTTP. Swagger2Markup supports the Swagger 1.2 and 2.0 specification. Internally it uses the official swagger-parser and my markup-document-builder.

You can use Swagger2Markup to convert your contract-first Swagger YAML file into a human-readable format and combine it with hand-written documentation. As an alternative, you can choose the code-first approach and use Swagger2Markup together with Swagger JAX-RS, springfox or spring-restdocs. See usage guide below. If you are are Gradle user, you can also use the Swagger2Markup Gradle Plugin.

AsciiDoc is preferable to Markdown as it has more features. AsciiDoc is a text document format for writing documentation, articles, books, ebooks, slideshows, web pages and blogs. AsciiDoc files can be converted to HTML, PDF and EPUB. AsciiDoc is much better suited for describing public APIs than JavaDoc or Annotations.

You can generate your HTML5, PDF and EPUB documentation via asciidoctorj or even better via the asciidoctor-gradle-plugin or asciidoctor-maven-plugin. You can also use JBake, MkDocs, ReadTheDocs or slate to publish your AsciiDoc or Markdown documentation.

The project requires at least JDK 7.

Usage guide

Adding Swagger2Markup to your project

The project is published in JCenter and Maven Central.

Maven

<repositories>
    <repository>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <id>central</id>
        <name>bintray</name>
        <url>http://jcenter.bintray.com</url>
    </repository>
</repositories>

<dependency>
    <groupId>io.github.robwin</groupId>
    <artifactId>swagger2markup</artifactId>
    <version>0.6.2</version>
</dependency>

Gradle

repositories {
    jcenter()
}

compile "io.github.robwin:swagger2markup:0.6.2"

Using Swagger2Markup

Using the Swagger2MarkupConverter is simple. For example, if you are using Spring Boot and springfox, you can generate your Swagger JSON file during the integration or unit test phase, convert the Swagger JSON file into AsciiDoc, convert AsciiDoc into HTML and PDF, copy the documentation into the Jar file and serve it as static content. That way there is no runtime overhead and there are no additional runtime libraries required.
The quickest way to get started is to look at the demo project spring-swagger2markup-demo. The demo shows how to generate static docs (HTML5 and PDF) with the Swagger2Markup Gradle Plugin and serve them as static content in a Spring Boot App under http://localhost:9080/docs/index.html and http://localhost:9080/docs/index.pdf.

Generate Markup during an integration test

Swagger2MarkupConverter can be used to make a request to a Swagger endpoint during an integration test. The Swagger2MarkupConverter writes the generated documents into the folder src/docs/asciidoc/generated or src/docs/markdown/generated.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@IntegrationTest
@WebAppConfiguration
public class Swagger2MarkupTest {

    @Test
    public void convertRemoteSwaggerToAsciiDoc() {
        // Remote Swagger source
        // Default is AsciiDoc
        Swagger2MarkupConverter.from("http://localhost:8080/v2/api-docs").build()
            .intoFolder("src/docs/asciidoc/generated");

        // Then validate that three AsciiDoc files have been created
        String[] files = new File("src/docs/asciidoc/generated").list();
        assertThat(files).hasSize(3)
            .containsAll(Arrays.asList("definitions.adoc", "overview.adoc", "paths.adoc"));
    }

    @Test
    public void convertRemoteSwaggerToMarkdown() {
        // Remote Swagger source
        // Markdown
        Swagger2MarkupConverter.from("http://localhost:8080/v2/api-docs")
            .withMarkupLanguage(MarkupLanguage.MARKDOWN).build()
            .intoFolder("src/docs/markdown/generated");

        // Then validate that three Markdown files have been created
        String[] files = new File("src/docs/markdown/generated").list();
        assertThat(files).hasSize(3)
            .containsAll(Arrays.asList("definitions.md", "overview.md", "paths.md"));
    }

    @Test
    public void convertLocalSwaggerToAsciiDoc() {
        //Local Swagger source
        //Default is AsciiDoc
        String location = Swagger2MarkupTest.class.getResource("/json/swagger.json").getPath();
        Swagger2MarkupConverter.from(location).build()
            .intoFolder("src/docs/asciidoc/generated");
    }
}

Generate Markup during an unit test with springfox-staticdocs

Spring’s MVC Test framework can also be used to make a request to a springfox Swagger endpoint during an unit test. A custom ResultHandler Swagger2MarkupResultHandler is used to automatically convert the Swagger JSON response into an AsciiDoc document. The custom ResultHandler is part of springfox-staticdocs. That way you also verify that your Swagger endpoint is working.

@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
public class Swagger2MarkupTest {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    @Test
    public void convertSwaggerToAsciiDoc() throws Exception {
        this.mockMvc.perform(get("/v2/api-docs")
                .accept(MediaType.APPLICATION_JSON))
                .andDo(Swagger2MarkupResultHandler.outputDirectory("src/docs/asciidoc/generated").build())
                .andExpect(status().isOk());
    }

    @Test
    public void convertSwaggerToMarkdown() throws Exception {
        this.mockMvc.perform(get("/v2/api-docs")
                .accept(MediaType.APPLICATION_JSON))
                .andDo(Swagger2MarkupResultHandler.outputDirectory("src/docs/markdown/generated")
                    .withMarkupLanguage(MarkupLanguage.MARKDOWN).build())
                .andExpect(status().isOk());
    }
}

Gradle dependencies

dependencies {
    ...
    compile 'io.springfox:springfox-swagger2:2.0.0'
    testCompile 'io.springfox:springfox-staticdocs:2.0.0'
    ...
}

Springfox configuration

The following is a complete springfox configuration to use Swagger in a Spring Boot Application.

@SpringBootApplication
@EnableSwagger2
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Docket restApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .paths(ant("/api/**"))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Petstore API Title")
                .description("Petstore API Description")
                .contact("Petstore API Contact Email")
                .version("1.0.0")
                .build();
    }
}

Combine generated documentation with your hand-written documentation

The following shows how you can combine the generated documentation with your hand-written AsciiDoc documentation. You have to create an index.adoc (it must not be necessarily called index). To include the programmatically generated snippets in your documentation, you use Asciidoc’s include macro. The generated variable is configured below.

generated_docs

You can generate your HTML5 and PDF documentation via the asciidoctor-gradle-plugin or asciidoctor-maven-plugin. The following listing shows how to configure the Asciidoctor Gradle plugin. By default it searches for AsciiDoc files in src/docs/asciidoc and puts the HTML and PDF output into build/asciidoc/html5 and build/asciidoc/pdf. The generated attribute is used to replace the variable in the index.adoc file and to provide configurable access to the generated snippets.

ext {
    generatedDocumentation = file('src/docs/asciidoc/generated')
}

test {
    outputs.dir generatedDocumentation
}

asciidoctor {
    dependsOn test
    sources {
        include 'index.adoc'
    }
    backends = ['html5', 'pdf']
    attributes = [
            doctype: 'book',
            toc: 'left',
            toclevels: '2',
            numbered: '',
            sectlinks: '',
            sectanchors: '',
            generated: generatedDocumentation
    ]
}

You can copy the output into your Jar file and serve the documentation as static content under http://localhost:9080/docs/index.html and http://localhost:9080/docs/index.pdf.

jar {
    dependsOn asciidoctor
    from ("${asciidoctor.outputDir}/html5") {
        into 'static/docs'
    }
    from ("${asciidoctor.outputDir}/pdf") {
        into 'static/docs'
    }
}

Include spring-restdocs example snippets

Swagger2Markup can be used together with spring-restdocs. Swagger2Markup can include the generated CURL request, HTTP request and HTTP response example snippets from spring-restdocs into the generated AsciiDoc document. See spring-restdocs how to configure it. Currently spring-restdocs does only support AsciiDoc.

Let’s say you have a Swagger-annotated Spring RestController method with an ApiOperation value: Add a new pet to the store

@RequestMapping(method = POST)
@ApiOperation(value = "Add a new pet to the store")
@ApiResponses(value = {@ApiResponse(code = 405, message = "Invalid input")})
public ResponseEntity<String> addPet(
      @ApiParam(value = "Pet object that needs to be added to the store", required = true) @RequestBody Pet pet) {
    petData.add(pet);
    return Responses.ok("SUCCESS");
}

By convention the target folder of the generated request and response example files must be similar to the value of the ApiOperation, but with underscores and lowercase. For example a folder for @ApiOperation(value = "Add a new pet to the store") must be called add_a_new_pet_to_the_store.

@Test
public void findPetById() throws Exception {
    this.mockMvc.perform(post("/api/pet/").content(createPet())
            .contentType(MediaType.APPLICATION_JSON))
            .andDo(RestDocumentation.document("add_a_new_pet_to_the_store"))
            .andExpect(status().isOk());
}

The system property org.springframework.restdocs.outputDir is used to control the output base directory where the folder add_a_new_pet_to_the_store and the generated snippets are written to. The spring-restdocs output base directory is configured as follows:

ext {
    generatedDocumentation = file('src/docs/asciidoc/generated')
}

test {
    systemProperty 'org.springframework.restdocs.outputDir', generatedDocumentation
    outputs.dir generatedDocumentation
}

You must specify the base output directory of spring-restdocs with the builder method withExamples("src/docs/asciidoc/generated").

@Test
public void convertToAsciiDoc() throws Exception {
    this.mockMvc.perform(get("/v2/api-docs")
            .accept(MediaType.APPLICATION_JSON))
            .andDo(Swagger2MarkupResultHandler.outputDirectory("src/docs/asciidoc")
                    .withExamples("src/docs/asciidoc/generated").build())
            .andExpect(status().isOk());
}

By convention the Swagger2MarkupConverter searches for a method annotated with @ApiOperation(value = "Add a new pet to the store") in a folder called src/docs/asciidoc/generated/add_a_new_pet_to_the_store and includes the http-request.adoc and http-response.adoc files, if they are available.

The AsciiDoc HTML output would look as follows:

springrestdocs

Include hand-written descriptions into the generated documentation

If you don’t want to pollute your source code with Swagger annotations just to add descriptions to Operations, Parameters and Model definitions. Like here:

@RequestMapping(method = PUT)
@ApiOperation(value = "Update an existing pet")
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid ID supplied"),
      @ApiResponse(code = 404, message = "Pet not found"),
      @ApiResponse(code = 405, message = "Validation exception")})
public ResponseEntity<String> updatePet(
      @ApiParam(value = "Pet object that needs to be added to the store", required = true) @RequestBody Pet pet) {
    petData.add(pet);
    return Responses.ok("SUCCESS");
}

You can create hand-written descriptions and include them with the builder method withDescriptions() into your documentation by specifying the base folder of your documentation.

Swagger2MarkupConverter.from(file.getAbsolutePath()).withDescriptions("src/docs/asciidoc").build()
            .intoFolder("src/docs/asciidoc");

By convention you need two folders paths and definitions inside your description base folder.
The paths folder contains sub folders for all operations. The folder must be named similar to the value of the ApiOperation annotation, but with underscores and lowercase.
For example a folder for @ApiOperation(value = "Update an existing pet") must be called update_an_existing_pet.

The definitions folder contains sub folders for all models. The folder must be named similar to the name of the Model, but lowercase.
For example a folder for a model called User must be called user.

You can have a global description file for each operation or model. And you can have one description file per operation parameter or model property.
For example:

handwritten_descriptions

The AsciiDoc HTML output would look as follows:

handwritten_descr_asciidoc

Include JSON and XML Schema files.

Swagger2Markup can also include JSON and XML Schema files into the generated document.

Swagger2MarkupConverter.from("http://localhost:8080/api-docs").withSchemas("src/docs/schemas").build()
                .intoFolder("src/docs/asciidoc");

You can create the schema files during a unit test as follows:

RestDocumented restDocumented = RestDocumented.fromProperties();
restDocumented.documentJsonSchema(MailStorageQuota.class, "src/docs/schemas");
restDocumented.documentXmlSchema(MailStorageQuota.class, "src/docs/schemas");

I will make RestDocumented public soon. RestDocumented creates a MailStorageQuota.xsd and MailStorageQuota.json file in the folder src/docs/schemas. The Swagger2MarkupConverter will include the JSON and XML Schemas, if a Swagger Operation uses the MailStorageQuota class as input or output.

Screenshots

Swagger source

swagger_json

Generated AsciiDoc

asciidoc

Generated Markdown

markdown

Generated HTML using AsciidoctorJ

asciidoc_html

Generated PDF using AsciidoctorJ

asciidoc_pdf

License

Copyright 2015 Robert Winkler

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.

About

A Swagger2Markup (AsciiDoc/Markdown) converter to simplify the generation of an up-to-date RESTful API documentation by combining documentation that’s been hand-written with auto-generated API documentation.

License:Apache License 2.0


Languages

Language:Java 100.0%