7 months ago

Swagger 在工程師或測試方面幫助很大,但有些人員非工程背景,拿到 Jar 檔也不會執行,也就看不到 API 規格,所以還是需要一份文件,這時候我們既然都定義好 Swagger 了,直接用來輸出就最方便

流程來說是 測試程式產出 adoc 檔 -> plugins org.asciidoctor.convert 轉換成 html 等其他格式

build.gradle
buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'org.asciidoctor.convert' version '1.5.2'
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

jar {
    baseName = 'ps-account'
    version = '0.0.1-SNAPSHOT'
    dependsOn asciidoctor
}

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-data-rest')
    compile('org.springframework.boot:spring-boot-starter-mail')
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile 'org.springframework.security.oauth:spring-security-oauth2:2.0.12.RELEASE'
    compile 'org.springframework.security:spring-security-jwt:1.0.7.RELEASE'
    compile 'org.modelmapper:modelmapper:0.7.7'
    compile 'org.apache.commons:commons-lang3:3.5'
    compile 'mysql:mysql-connector-java:6.0.5'
    compile 'io.springfox:springfox-swagger2:2.6.1'
    compile 'io.springfox:springfox-swagger-ui:2.6.1'
    compile 'io.springfox:springfox-data-rest:2.6.1'
    compileOnly('org.projectlombok:lombok')
    testCompile 'io.springfox:springfox-staticdocs:2.6.1'
    testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc:1.1.2.RELEASE'
    testRuntime('com.h2database:h2')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

asciidoctor {
    dependsOn test
    sourceDir 'src/docs/asciidoc'
    attributes 'snippets': file("build/asciidoc"),
            'doctype': 'book',
            'icons': 'font',
            'source-highlighter': 'highlightjs',
            'toc': 'left',
            'toclevels': 2,
            'sectlinks': true
    outputDir = file('build/asciidoc/generated')
//    backends 'html5', 'pdf', 'epub3', 'docbook' , 'revealjs'
//    sourceDir = file('src/docs/asciidoc')
//    outputDir = file('build/asciidoc/generated')
}
  • 首先加入 plugins { id 'org.asciidoctor.convert' version '1.5.2' }
  • 再來加入
    • testCompile 'io.springfox:springfox-staticdocs:2.6.1'
    • testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc:1.1.2.RELEASE'
  • 定義 asciidoctor 區塊,也就是轉換成 html 的樣式
  • jar 區塊加上 dependsOn asciidoctor 這樣一來我們在打包時就可以順便轉出一份文檔

如果需要其他格式,可以再加上 backends 'html5', 'pdf', 'epub3', 'docbook' , 'revealjs'
這樣一來就可以了...but 理論上是這樣,不過除了 html 以外其他我試不出來,應該是配置有些不同吧
目前沒用到就不研究了

sourceDir 'src/docs/asciidoc' 這部分呢是指來源資料夾
但是 Swagger 預設會輸出三份 adoc ,這樣就會變成三份分開的文件
所以需要手動編排一下像下面

編排的內容如下,簡單用就是把產出的文件包進來

index.adoc
= {project-name} Getting Started Guide

:revnumber: {project-version}

include::introduction.adoc[]

include::generated/overview.adoc[]
include::generated/paths.adoc[]
include::generated/definitions.adoc[]

[[resources]]
= Resources

// \include::resources/some-resource.adoc[] 註解
introduction.adoc
{project-name} is a RESTful web service for manager user account & permission.

最後準備產出 adoc 的測試程式

Swagger2MarkupTest.java
package com.ps;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.github.robwin.swagger2markup.GroupBy;
import io.github.robwin.swagger2markup.Swagger2MarkupConverter;
import org.apache.commons.lang3.Validate;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

/**
 * Created by samchu on 2017/3/28.
 */

@AutoConfigureMockMvc
@RunWith(SpringRunner.class)
@SpringBootTest
public class Swagger2MarkupTest {

    @Autowired
    private MockMvc mockMvc;

    @Before
    public void setUp() {

    }

    // 輸出成 adoc 格式

    @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())

                // 稍微客製化只留下api開頭的path

                .andDo(new PsSwagger2MarkupHandler("src/docs/asciidoc/generated"))
                .andExpect(status().isOk());
    }

    // 輸出成 Markdown 格式

//    @Test

//    public void convertSwaggerToMarkdown() throws Exception {

//        this.mockMvc.perform(get("/v2/api-docs")

//                .accept(MediaType.APPLICATION_JSON))

//                .andDo(Swagger2MarkupResultHandler.outputDirectory("docs/markdown/generated")

//                        .withMarkupLanguage(MarkupLanguage.MARKDOWN).build())

//                .andExpect(status().isOk());

//    }


    // 一般寫測試兼輸出的方式

//    @Test

//    public void testIndex() throws Exception {

//        this.mockMvc.perform(get("/api/test").accept(MediaType.APPLICATION_JSON))

//                .andExpect(status().isOk())

//                .andDo(document("test", preprocessResponse(prettyPrint())));

//    }


    class PsSwagger2MarkupHandler implements ResultHandler {
        private String outputDir;
        private final MarkupLanguage markupLanguage = MarkupLanguage.ASCIIDOC;
        private String examplesFolderPath;
        private final GroupBy pathsGroupedBy = GroupBy.AS_IS;
        private final String encoding = "UTF-8";
        private String passStr = "listRepositories,getCollectionResource,getItemResource,getCollectionResourceCompact,listSearches,errorHtml,listAllFormsOfMetadata,schema";


        public PsSwagger2MarkupHandler(String outputDir) {
            Validate.notEmpty(outputDir, "outputDir must not be empty!");
            this.outputDir = outputDir;
        }

        @Override
        public void handle(MvcResult result) throws Exception {
            MockHttpServletResponse response = result.getResponse();
            response.setCharacterEncoding(encoding);
            String swaggerJson = response.getContentAsString();
            // 去除掉 Spring Data Rest 提供的基礎路徑

            List<String> passSummary = Arrays.asList(passStr.split(","));
            ObjectMapper mapper = new ObjectMapper();
            ObjectNode newPaths = mapper.createObjectNode();
            JsonNode jsonRoot = mapper.readTree(swaggerJson);
            JsonNode jsonPaths = jsonRoot.get("paths");
            Iterator<String> keyit = jsonPaths.fieldNames();
            while (keyit.hasNext()) {
                String key = keyit.next();
                JsonNode jsonPath = jsonPaths.get(key);
                String summary = jsonPath.findPath("summary").asText();
                if (!passSummary.contains(summary)) {
                    newPaths.set(key, jsonPath);
                }
            }
            ((ObjectNode) jsonRoot).set("paths", newPaths);
            swaggerJson = jsonRoot.toString();
            // 這邊輸出成 adoc

            Swagger2MarkupConverter.fromString(swaggerJson).withMarkupLanguage(markupLanguage)
                    .withPathsGroupedBy(this.pathsGroupedBy)
                    .withExamples(examplesFolderPath).build().intoFolder(outputDir);
        }
    }
}

這樣大致就完成,執行 gradle test 會產出 adoc ,執行 jar 會轉成文檔的格式
如果是 html 會是在這裡 build\asciidoc\generated\html5 像這樣


就可以打包提供給相關人員囉

參考資料
asciidoctor/asciidoctor-gradle-plugin: A Gradle plugin that uses Asciidoctor via JRuby to process AsciiDoc source files within the project.
jhipster Static API documentation
BeeWorks - Bootiful CMS part 3 - Microservice with Test-Driven Documentation

← Spring Test Use H2 And TestRestTemplate Ubuntu 16.04.2 install VPN use shadowsocks →
 
comments powered by Disqus