Compare commits

...

19 Commits

Author SHA1 Message Date
Robert Winkler
6c50292563 Update version number to 0.2.0 2015-02-19 11:38:16 +01:00
Robert Winkler
3d116dd7f8 Updated version number 2015-02-19 11:09:24 +01:00
Robert Winkler
e6f7372c9f Updated documentation 2015-02-19 11:09:02 +01:00
Robert Winkler
0ca91ea930 Swagger2MarkupConverter refactored.
Supports examples and schemas includes now.
2015-02-19 10:48:41 +01:00
Robert Winkler
efea76d243 Updated documentation 2015-02-16 13:51:42 +01:00
Robert Winkler
cd4ef60cc3 Updated documentation 2015-02-16 12:55:38 +01:00
Robert Winkler
6ed557b4ac Updated documentation 2015-02-16 11:49:23 +01:00
Robert Winkler
139c294f25 Updated documentation 2015-02-16 11:40:58 +01:00
Robert Winkler
68a433b76b Updated documentation 2015-02-16 11:38:46 +01:00
Robert Winkler
b0dba05b48 Fixed documentation 2015-02-16 10:23:44 +01:00
Robert Winkler
55bb2bdd71 Added released version 2015-02-16 10:21:31 +01:00
Robert Winkler
3d4d87c0b0 Merge branch 'master' into develop 2015-02-16 10:02:17 +01:00
Robert Winkler
ad0d03c95c Fixed bintray set-up 2015-02-16 10:01:22 +01:00
Robert Winkler
0675beaa45 Merge branch 'release/v0.1.0' into develop 2015-02-16 09:52:44 +01:00
Robert Winkler
cb6526d0e5 Merge branch 'release/v0.1.0' 2015-02-16 09:52:44 +01:00
Robert Winkler
f5a8454817 Updated README 2015-02-16 09:17:44 +01:00
Robert Winkler
5bf8270f2e Fixed readme 2015-02-13 15:28:36 +01:00
Robert Winkler
6c794e75fd Fixed image links 2015-02-13 15:20:05 +01:00
Robert Winkler
b472bf97bb Fixed Image links 2015-02-13 15:15:22 +01:00
29 changed files with 893 additions and 1296 deletions

View File

@@ -3,9 +3,7 @@
:version: 0.1.0 :version: 0.1.0
:hardbreaks: :hardbreaks:
image:https://travis-ci.org/RobWin/swagger2markup.svg["Build Status", link="https://travis-ci.org/RobWin/swagger2markup"] image:https://coveralls.io/repos/RobWin/swagger2markup/badge.svg["Coverage Status", link="https://coveralls.io/r/RobWin/swagger2markup"] image:http://img.shields.io/:version-{version}-blue.svg["Semantic Versioning", link="http://semver.org"] image:http://img.shields.io/badge/license-ASF2-blue.svg["Apache License 2", link="http://www.apache.org/licenses/LICENSE-2.0.txt"] image:https://travis-ci.org/RobWin/swagger2markup.svg["Build Status", link="https://travis-ci.org/RobWin/swagger2markup"] image:https://coveralls.io/repos/RobWin/swagger2markup/badge.svg["Coverage Status", link="https://coveralls.io/r/RobWin/swagger2markup"] image:http://img.shields.io/:version-{version}-blue.svg["Semantic Versioning", link="https://bintray.com/robwin/maven/swagger2markup/0.1.0/view"] image:http://img.shields.io/badge/license-ASF2-blue.svg["Apache License 2", link="http://www.apache.org/licenses/LICENSE-2.0.txt"]
WARNING: Still under heavy development
== Overview == Overview
@@ -15,7 +13,42 @@ The primary goal of this project is to simplify the documentation of RESTful API
== Usage == Usage
Using the Swagger2MarkupConverter is simple. For instance, you can generate your AsciiDoc/Markdown documentation using https://github.com/spring-projects/spring-boot[Spring Boot] and https://github.com/martypitt/swagger-springmvc[swagger-springmvc] as follows: === Adding Swagger2Markup to your project
==== Maven
[source,xml]
----
<repositories>
<repository>
<id>jcenter-release</id>
<name>jcenter</name>
<url>http://oss.jfrog.org/artifactory/oss-release-local/</url>
</repository>
</repositories>
<dependency>
<groupId>io.github.robwin.swagger2markup</groupId>
<artifactId>swagger2markup</artifactId>
<version>0.2.0</version>
</dependency>
----
==== Gradle
[source,groovy]
----
repositories {
jcenter()
}
compile "io.github.robwin:swagger2markup:0.2.0"
----
=== Using Swagger2Markup
Using the Swagger2MarkupConverter is simple. For instance, you can generate your AsciiDoc/Markdown documentation using https://github.com/spring-projects/spring-boot[Spring Boot] and https://github.com/martypitt/swagger-springmvc[swagger-springmvc] as follows.
See demo project https://github.com/RobWin/spring-swagger2markup-demo[spring-swagger2markup-demo].
[source,java] [source,java]
---- ----
@@ -29,16 +62,19 @@ public class Swagger2MarkupTest {
public void convertSwaggerToMarkup() { public void convertSwaggerToMarkup() {
//Remote //Remote
Swagger2MarkupConverter.from("http://localhost:8080/api-docs"). Swagger2MarkupConverter.from("http://localhost:8080/api-docs").
toAsciiDoc("src/docs/asciidoc/example.adoc"); withMarkupLanguage(MarkupLanguage.MARKDOWN).
withExamples("docs/generated").withSchemas("docs/schemas").build()
.intoFolder("src/docs/markdown");
Swagger2MarkupConverter.from("http://localhost:8080/api-docs"). Swagger2MarkupConverter.from("http://localhost:8080/api-docs").
toMarkdown("src/docs/markdown/example.md"); withExamples("docs").withSchemas("docs/schemas").build()
.intoFolder("src/docs/asciidoc");
//Local //Local
File file = new File(Swagger2MarkupTest.class.getResource("/json/swagger.json").getFile()); File file = new File(Swagger2MarkupTest.class.getResource("/json/swagger.json").getFile());
Swagger2MarkupConverter.from(file.getAbsolutePath()).toAsciiDoc("src/docs/asciidoc/swagger.adoc") Swagger2MarkupConverter.from(file.getAbsolutePath()).build()
.intoFolder("src/docs/asciidoc");
} }
} }
---- ----
@@ -75,30 +111,36 @@ public class SpringBootSwaggerConfig {
} }
---- ----
You can then generate HTML5 and PDF documentation via https://github.com/asciidoctor/asciidoctorj[asciidoctorj] or even better via the https://github.com/asciidoctor/asciidoctor-gradle-plugin[asciidoctor-gradle-plugin] and https://github.com/aalmiray/markdown-gradle-plugin[markdown-gradle-plugin]. You can generate your HTML5 and PDF documentation via https://github.com/asciidoctor/asciidoctorj[asciidoctorj] or even better via the https://github.com/asciidoctor/asciidoctor-gradle-plugin[asciidoctor-gradle-plugin] or https://github.com/aalmiray/markdown-gradle-plugin[markdown-gradle-plugin].
You can also use https://github.com/tomchristie/mkdocs[MkDocs] and https://github.com/rtfd/readthedocs.org[ReadTheDocs] to publish your Markdown documentation.
See http://spring-swagger2markup-demo.readthedocs.org/[ReadTheDocs-demo]
== Example == Examples
== swagger.json == Swagger source file
image::images/swagger_json.PNG[swagger_json] image::images/swagger_json.PNG[swagger_json]
=== Generated AsciiDoc file === Generated AsciiDoc file
image::images/asciidoc.PNG[asciidoc] image::images/asciidoc.PNG[asciidoc]
=== Generated Markdown file === Generated Markdown file
image::images/markdown.PNG[asciidoc] image::images/markdown.PNG[markdown]
=== Generated HTML === Generated HTML using AsciidoctorJ
image::images/asciidoc_html.PNG[asciidoc_html] image::images/asciidoc_html.PNG[asciidoc_html]
=== Generated HTML using Mkdocs
image::images/mkdocs_html.PNG[mkdocs_html]
=== Generated PDF using AsciidoctorJ
image::images/asciidoc_pdf.PNG[asciidoc_pdf]
== Document Builder == Document Builder
The converter allows to build an AsciiDoc or Markdown document via the Builder pattern: The Swagger2Markup library allows to build an AsciiDoc or Markdown document via the Builder pattern:
[source,java] [source,java]
---- ----
String asciiDoc = new AsciiDocBuilder().documentTitle("Title") DocumentBuilder builder = DocumentBuilders.documentBuilder(MarkupLanguage.ASCIIDOC);
.sectionTitleLevel1("Section1").paragraph("Text text") builder.documentTitle("Test title").textLine("Text line").writeToFile("/tmp", "test.adoc", StandardCharsets.UTF_8);
.sectionTitleLevel2("Code examples").listing("Code example").toString();
String markdown = new MarkdownBuilder().documentTitle("Title") DocumentBuilder builder = DocumentBuilders.documentBuilder(MarkupLanguage.MARKDOWN);
.sectionTitleLevel1("Section1").paragraph("Text text") builder.documentTitle("Test title").textLine("Text line").writeToFile("/tmp", "test.adoc", StandardCharsets.UTF_8);
.sectionTitleLevel2("Code examples").listing("Code example").toString();
---- ----

View File

@@ -1,4 +1,7 @@
= Release Notes = Release Notes
== Version 0.1.0 == Version 0.1.0
* Initial version with support for AsciiDoc and Markdown * Initial version with support for AsciiDoc and Markdown
== Version 0.2.0
* This version is not downward compatible. This version supports includes of example files and JSON/XML Schema files. See documentation.

View File

@@ -8,20 +8,16 @@ buildscript {
classpath 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.6' classpath 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.6'
classpath 'io.spring.gradle:dependency-management-plugin:0.3.1.RELEASE' classpath 'io.spring.gradle:dependency-management-plugin:0.3.1.RELEASE'
classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.0.1' classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.0.1'
classpath 'me.champeau.gradle:jbake-gradle-plugin:0.2'
classpath 'org.jbake:jbake-core:2.3.2'
classpath 'org.asciidoctor:asciidoctorj:1.5.2' classpath 'org.asciidoctor:asciidoctorj:1.5.2'
classpath 'org.freemarker:freemarker:2.3.19'
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
} }
} }
description = 'swagger2markup Build' description = 'swagger2markup Build'
version = '0.1.0' version = '0.2.0'
group = 'io.swagger2markup' group = 'io.github.robwin'
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'maven' apply plugin: 'maven-publish'
apply plugin: 'org.asciidoctor.convert' apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'com.github.kt3k.coveralls' apply plugin: 'com.github.kt3k.coveralls'
@@ -40,12 +36,13 @@ tasks.withType(JavaCompile) {
repositories { repositories {
jcenter() jcenter()
mavenCentral() mavenCentral()
mavenLocal()
} }
dependencies { dependencies {
compile 'io.swagger:swagger-compat-spec-parser' compile 'io.swagger:swagger-compat-spec-parser'
compile 'commons-collections:commons-collections' compile 'commons-collections:commons-collections'
compile 'commons-io:commons-io'
compile 'ch.qos.logback:logback-classic'
testCompile 'junit:junit' testCompile 'junit:junit'
} }
@@ -53,17 +50,21 @@ dependencyManagement {
dependencies { dependencies {
"io.swagger:swagger-compat-spec-parser" "1.0.0" "io.swagger:swagger-compat-spec-parser" "1.0.0"
"commons-collections:commons-collections" "3.2.1" "commons-collections:commons-collections" "3.2.1"
"commons-io:commons-io" "2.4"
"com.mangofactory:swagger-springmvc" "0.9.5" "com.mangofactory:swagger-springmvc" "0.9.5"
"com.jayway.restassured:spring-mock-mvc" "2.4.0" "com.jayway.restassured:spring-mock-mvc" "2.4.0"
"ch.qos.logback:logback-classic" "1.1.2"
"junit:junit" "4.11" "junit:junit" "4.11"
} }
} }
asciidoctor { asciidoctor {
backends = ['html5', 'pdf']
attributes = [ attributes = [
doctype: 'book', doctype: 'book',
toc: 'left', toc: 'left',
toclevels: '1' toclevels: '2',
numbered: ''
] ]
} }
@@ -78,6 +79,10 @@ tasks.coveralls {
dependsOn 'check' dependsOn 'check'
} }
tasks.asciidoctor {
dependsOn 'check'
}
task wrapper(type: Wrapper) { task wrapper(type: Wrapper) {
gradleVersion = '2.2.1' gradleVersion = '2.2.1'
} }

View File

@@ -29,5 +29,47 @@ if (!project.hasProperty('bintrayApiKey')) ext.bintrayApiKey = ''
bintray { bintray {
user = project.bintrayUsername user = project.bintrayUsername
key = project.bintrayApiKey key = project.bintrayApiKey
pkg.repo = 'swagger2markup' publications = ['mavenJava']
pkg {
repo = 'maven'
name = 'swagger2markup'
websiteUrl = 'https://github.com/RobWin/swagger2markup'
issueTrackerUrl = 'https://github.com/RobWin/swagger2markup/issues'
vcsUrl = 'https://github.com/RobWin/swagger2markup.git'
desc = 'A Swagger to Markup (AsciiDoc and Markdown) converter.'
licenses = ['Apache-2.0']
version {
vcsTag = project.version
}
}
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
pom.withXml {
def devs = ['RobWin': 'Robert Winkler']
def root = asNode()
root.appendNode('name', 'swagger2markup')
root.appendNode('packaging', 'jar')
root.appendNode('url', 'https://github.com/RobWin/swagger2markup')
root.appendNode('description', 'A Swagger to Markup (AsciiDoc and Markdown) converter.')
def license = root.appendNode('licenses').appendNode('license')
license.appendNode('name', 'Apache-2.0')
license.appendNode('url', 'https://github.com/RobWin/swagger2markup/blob/master/LICENSE.txt')
license.appendNode('distribution', 'repo')
root.appendNode('scm').appendNode('url', 'https://github.com/RobWin/swagger2markup.git')
def developers = root.appendNode('developers')
devs.each {
def d = developers.appendNode('developer')
d.appendNode('id', it.key)
d.appendNode('name', it.value)
}
}
}
}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 105 KiB

BIN
images/asciidoc_pdf.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
images/mkdocs_html.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -1,534 +0,0 @@
= Swagger Petstore
This is a sample server Petstore server.
[Learn about Swagger](http://swagger.wordnik.com) or join the IRC channel `#swagger` on irc.freenode.net.
For this sample, you can use the api key `special-key` to test the authorization filters
Version: 1.0.0
== Update an existing pet
----
PUT /pets
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
body,body,Pet object that needs to be added to the store,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
400,Invalid ID supplied
404,Pet not found
405,Validation exception
|===
=== Consumes
* application/json
* application/xml
=== Produces
* application/json
* application/xml
== Add a new pet to the store
----
POST /pets
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
body,body,Pet object that needs to be added to the store,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
405,Invalid input
|===
=== Consumes
* application/json
* application/xml
=== Produces
* application/json
* application/xml
== Finds Pets by status
----
GET /pets/findByStatus
----
=== Description
:hardbreaks:
Multiple status values can be provided with comma seperated strings
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
status,query,Status values that need to be considered for filter,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid status value
|===
=== Produces
* application/json
* application/xml
== Finds Pets by tags
----
GET /pets/findByTags
----
=== Description
:hardbreaks:
Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
tags,query,Tags to filter by,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid tag value
|===
=== Produces
* application/json
* application/xml
== Find pet by ID
----
GET /pets/{petId}
----
=== Description
:hardbreaks:
Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
petId,path,ID of pet that needs to be fetched,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid ID supplied
404,Pet not found
|===
=== Produces
* application/json
* application/xml
== Deletes a pet
----
DELETE /pets/{petId}
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
api_key,header,,true
petId,path,Pet id to delete,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
400,Invalid pet value
|===
=== Produces
* application/json
* application/xml
== Updates a pet in the store with form data
----
POST /pets/{petId}
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
petId,path,ID of pet that needs to be updated,true
name,formData,Updated name of the pet,true
status,formData,Updated status of the pet,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
405,Invalid input
|===
=== Consumes
* application/x-www-form-urlencoded
=== Produces
* application/json
* application/xml
== Place an order for a pet
----
POST /stores/order
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
body,body,order placed for purchasing the pet,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid Order
|===
=== Produces
* application/json
* application/xml
== Find purchase order by ID
----
GET /stores/order/{orderId}
----
=== Description
:hardbreaks:
For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
orderId,path,ID of pet that needs to be fetched,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid ID supplied
404,Order not found
|===
=== Produces
* application/json
* application/xml
== Delete purchase order by ID
----
DELETE /stores/order/{orderId}
----
=== Description
:hardbreaks:
For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
orderId,path,ID of the order that needs to be deleted,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
400,Invalid ID supplied
404,Order not found
|===
=== Produces
* application/json
* application/xml
== Create user
----
POST /users
----
=== Description
:hardbreaks:
This can only be done by the logged in user.
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
body,body,Created user object,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
default,successful operation
|===
=== Produces
* application/json
* application/xml
== Creates list of users with given input array
----
POST /users/createWithArray
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
body,body,List of user object,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
default,successful operation
|===
=== Produces
* application/json
* application/xml
== Creates list of users with given input array
----
POST /users/createWithList
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
body,body,List of user object,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
default,successful operation
|===
=== Produces
* application/json
* application/xml
== Logs user into the system
----
GET /users/login
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
username,query,The user name for login,false
password,query,The password for login in clear text,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid username/password supplied
|===
=== Produces
* application/json
* application/xml
== Logs out current logged in user session
----
GET /users/logout
----
=== Responses
[format="csv", options="header"]
|===
Code,Description
default,successful operation
|===
=== Produces
* application/json
* application/xml
== Get user by user name
----
GET /users/{username}
----
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
username,path,The name that needs to be fetched. Use user1 for testing.,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
200,successful operation
400,Invalid username supplied
404,User not found
|===
=== Produces
* application/json
* application/xml
== Updated user
----
PUT /users/{username}
----
=== Description
:hardbreaks:
This can only be done by the logged in user.
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
username,path,name that need to be deleted,true
body,body,Updated user object,false
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
400,Invalid user supplied
404,User not found
|===
=== Produces
* application/json
* application/xml
== Delete user
----
DELETE /users/{username}
----
=== Description
:hardbreaks:
This can only be done by the logged in user.
=== Parameters
[format="csv", options="header"]
|===
Name,Located in,Description,Required
username,path,The name that needs to be deleted,true
|===
=== Responses
[format="csv", options="header"]
|===
Code,Description
400,Invalid username supplied
404,User not found
|===
=== Produces
* application/json
* application/xml
== Definitions
=== User
[format="csv", options="header"]
|===
Name,Type,Required
id,integer,false
username,string,false
firstName,string,false
lastName,string,false
email,string,false
password,string,false
phone,string,false
userStatus,integer,false
|===
=== Category
[format="csv", options="header"]
|===
Name,Type,Required
id,integer,false
name,string,false
|===
=== Pet
[format="csv", options="header"]
|===
Name,Type,Required
id,integer,false
category,ref,false
name,string,true
photoUrls,array,true
tags,array,false
status,string,false
|===
=== Tag
[format="csv", options="header"]
|===
Name,Type,Required
id,integer,false
name,string,false
|===
=== Order
[format="csv", options="header"]
|===
Name,Type,Required
id,integer,false
petId,integer,false
quantity,integer,false
shipDate,string,false
status,string,false
complete,boolean,false
|===

View File

@@ -1,494 +0,0 @@
# Swagger Petstore
This is a sample server Petstore server.
[Learn about Swagger](http://swagger.wordnik.com) or join the IRC channel `#swagger` on irc.freenode.net.
For this sample, you can use the api key `special-key` to test the authorization filters
Version: 1.0.0
## Update an existing pet
```
PUT /pets
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|body|body|Pet object that needs to be added to the store|false|
### Responses
|Code|Description|
|----|----|
|400|Invalid ID supplied|
|404|Pet not found|
|405|Validation exception|
### Consumes
* application/json
* application/xml
### Produces
* application/json
* application/xml
## Add a new pet to the store
```
POST /pets
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|body|body|Pet object that needs to be added to the store|false|
### Responses
|Code|Description|
|----|----|
|405|Invalid input|
### Consumes
* application/json
* application/xml
### Produces
* application/json
* application/xml
## Finds Pets by status
```
GET /pets/findByStatus
```
### Description
Multiple status values can be provided with comma seperated strings
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|status|query|Status values that need to be considered for filter|false|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid status value|
### Produces
* application/json
* application/xml
## Finds Pets by tags
```
GET /pets/findByTags
```
### Description
Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|tags|query|Tags to filter by|false|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid tag value|
### Produces
* application/json
* application/xml
## Find pet by ID
```
GET /pets/{petId}
```
### Description
Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|petId|path|ID of pet that needs to be fetched|true|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid ID supplied|
|404|Pet not found|
### Produces
* application/json
* application/xml
## Deletes a pet
```
DELETE /pets/{petId}
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|api_key|header||true|
|petId|path|Pet id to delete|true|
### Responses
|Code|Description|
|----|----|
|400|Invalid pet value|
### Produces
* application/json
* application/xml
## Updates a pet in the store with form data
```
POST /pets/{petId}
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|petId|path|ID of pet that needs to be updated|true|
|name|formData|Updated name of the pet|true|
|status|formData|Updated status of the pet|true|
### Responses
|Code|Description|
|----|----|
|405|Invalid input|
### Consumes
* application/x-www-form-urlencoded
### Produces
* application/json
* application/xml
## Place an order for a pet
```
POST /stores/order
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|body|body|order placed for purchasing the pet|false|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid Order|
### Produces
* application/json
* application/xml
## Find purchase order by ID
```
GET /stores/order/{orderId}
```
### Description
For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|orderId|path|ID of pet that needs to be fetched|true|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid ID supplied|
|404|Order not found|
### Produces
* application/json
* application/xml
## Delete purchase order by ID
```
DELETE /stores/order/{orderId}
```
### Description
For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|orderId|path|ID of the order that needs to be deleted|true|
### Responses
|Code|Description|
|----|----|
|400|Invalid ID supplied|
|404|Order not found|
### Produces
* application/json
* application/xml
## Create user
```
POST /users
```
### Description
This can only be done by the logged in user.
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|body|body|Created user object|false|
### Responses
|Code|Description|
|----|----|
|default|successful operation|
### Produces
* application/json
* application/xml
## Creates list of users with given input array
```
POST /users/createWithArray
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|body|body|List of user object|false|
### Responses
|Code|Description|
|----|----|
|default|successful operation|
### Produces
* application/json
* application/xml
## Creates list of users with given input array
```
POST /users/createWithList
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|body|body|List of user object|false|
### Responses
|Code|Description|
|----|----|
|default|successful operation|
### Produces
* application/json
* application/xml
## Logs user into the system
```
GET /users/login
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|username|query|The user name for login|false|
|password|query|The password for login in clear text|false|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid username/password supplied|
### Produces
* application/json
* application/xml
## Logs out current logged in user session
```
GET /users/logout
```
### Responses
|Code|Description|
|----|----|
|default|successful operation|
### Produces
* application/json
* application/xml
## Get user by user name
```
GET /users/{username}
```
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|username|path|The name that needs to be fetched. Use user1 for testing.|true|
### Responses
|Code|Description|
|----|----|
|200|successful operation|
|400|Invalid username supplied|
|404|User not found|
### Produces
* application/json
* application/xml
## Updated user
```
PUT /users/{username}
```
### Description
This can only be done by the logged in user.
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|username|path|name that need to be deleted|true|
|body|body|Updated user object|false|
### Responses
|Code|Description|
|----|----|
|400|Invalid user supplied|
|404|User not found|
### Produces
* application/json
* application/xml
## Delete user
```
DELETE /users/{username}
```
### Description
This can only be done by the logged in user.
### Parameters
|Name|Located in|Description|Required|
|----|----|----|----|
|username|path|The name that needs to be deleted|true|
### Responses
|Code|Description|
|----|----|
|400|Invalid username supplied|
|404|User not found|
### Produces
* application/json
* application/xml
## Definitions
### User
|Name|Type|Required|
|----|----|----|
|id|integer|false|
|username|string|false|
|firstName|string|false|
|lastName|string|false|
|email|string|false|
|password|string|false|
|phone|string|false|
|userStatus|integer|false|
### Category
|Name|Type|Required|
|----|----|----|
|id|integer|false|
|name|string|false|
### Pet
|Name|Type|Required|
|----|----|----|
|id|integer|false|
|category|ref|false|
|name|string|true|
|photoUrls|array|true|
|tags|array|false|
|status|string|false|
### Tag
|Name|Type|Required|
|----|----|----|
|id|integer|false|
|name|string|false|
### Order
|Name|Type|Required|
|----|----|----|
|id|integer|false|
|petId|integer|false|
|quantity|integer|false|
|shipDate|string|false|
|status|string|false|
|complete|boolean|false|

View File

@@ -0,0 +1,130 @@
package io.github.robwin.swagger2markup;
import com.wordnik.swagger.models.Swagger;
import io.github.robwin.swagger2markup.builder.document.DefinitionsDocument;
import io.github.robwin.swagger2markup.builder.document.PathsDocument;
import io.github.robwin.swagger2markup.builder.markup.MarkupLanguage;
import io.swagger.parser.SwaggerParser;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author Robert Winkler
*/
public class Swagger2MarkupConverter {
private static final Logger LOG = LoggerFactory.getLogger(Swagger2MarkupConverter.class);
private final Swagger swagger;
private final MarkupLanguage markupLanguage;
private final String examplesFolderPath;
private final String schemasFolderPath;
private static final String PATHS_DOCUMENT = "paths";
private static final String DEFINITIONS_DOCUMENT = "definitions";
/**
* @param markupLanguage the markup language which is used to generate the files
* @param swagger the Swagger object
* @param examplesFolderPath the folderPath where examples are stored
* @param schemasFolderPath the folderPath where (XML, JSON)-Schema files are stored
*/
Swagger2MarkupConverter(MarkupLanguage markupLanguage, Swagger swagger, String examplesFolderPath, String schemasFolderPath){
this.markupLanguage = markupLanguage;
this.swagger = swagger;
this.examplesFolderPath = examplesFolderPath;
this.schemasFolderPath = schemasFolderPath;
}
/**
* Creates a Swagger2MarkupConverter.Builder using a given Swagger source.
*
* @param swaggerSource the Swagger source. Can be a HTTP url or a path to a local file.
* @return a Swagger2MarkupConverter
*/
public static Builder from(String swaggerSource){
Validate.notEmpty(swaggerSource, "swaggerSource must not be null!");
return new Builder(swaggerSource);
}
/**
* Builds the document with the given markup language and stores
* the files in the given folder.
*
* @param targetFolderPath the target folder
* @throws IOException if the files cannot be written
*/
public void intoFolder(String targetFolderPath) throws IOException {
Validate.notEmpty(targetFolderPath, "folderPath must not be null!");
buildDocuments(targetFolderPath);
}
/**
* Writes a file for the Paths (API) and a file for the Definitions (Model)
* @param directory the directory where the generated file should be stored
* @throws IOException if a file cannot be written
*/
private void buildDocuments(String directory) throws IOException {
new PathsDocument(swagger, markupLanguage, examplesFolderPath).build().writeToFile(directory, PATHS_DOCUMENT, StandardCharsets.UTF_8);
new DefinitionsDocument(swagger, markupLanguage, schemasFolderPath).build().writeToFile(directory, DEFINITIONS_DOCUMENT, StandardCharsets.UTF_8);
}
public static class Builder{
private final Swagger swagger;
private String examplesFolderPath;
private String schemasFolderPath;
private MarkupLanguage markupLanguage = MarkupLanguage.ASCIIDOC;
/**
* Creates a Builder using a given Swagger source.
*
* @param swaggerSource the Swagger source. Can be a HTTP url or a path to a local file.
*/
Builder(String swaggerSource){
swagger = new SwaggerParser().read(swaggerSource);
}
public Swagger2MarkupConverter build(){
return new Swagger2MarkupConverter(markupLanguage, swagger, examplesFolderPath, schemasFolderPath);
}
/**
* Specifies the markup language which should be used to generate the files
*
* @param markupLanguage the markup language which is used to generate the files
* @return the Swagger2MarkupConverter.Builder
*/
public Builder withMarkupLanguage(MarkupLanguage markupLanguage){
this.markupLanguage = markupLanguage;
return this;
}
/**
* Include examples into the Paths document
*
* @param examplesFolderPath the path to the folder where the example documents reside
* @return the Swagger2MarkupConverter.Builder
*/
public Builder withExamples(String examplesFolderPath){
this.examplesFolderPath = examplesFolderPath;
return this;
}
/**
* Include (JSON, XML) schemas into the Definitions document
*
* @param schemasFolderPath the path to the folder where the schema documents reside
* @return the Swagger2MarkupConverter.Builder
*/
public Builder withSchemas(String schemasFolderPath){
this.schemasFolderPath = schemasFolderPath;
return this;
}
}
}

View File

@@ -0,0 +1,144 @@
package io.github.robwin.swagger2markup.builder.document;
import com.wordnik.swagger.models.Model;
import com.wordnik.swagger.models.Swagger;
import com.wordnik.swagger.models.properties.Property;
import io.github.robwin.swagger2markup.builder.markup.MarkupLanguage;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Project: swagger2markup
* Copyright: Deutsche Telekom AG
*
* @author Robert Winkler <robert.winkler@telekom.de>
* @since 2.0.0
*/
public class DefinitionsDocument extends MarkupDocument {
private static final String DEFINITIONS = "Definitions";
private static final List<String> IGNORED_DEFINITIONS = Arrays.asList("Void");
private static final String JSON_SCHEMA = "JSON Schema";
private static final String XML_SCHEMA = "XML Schema";
private static final String NAME_COLUMN = "Name";
private static final String TYPE_COLUMN = "Type";
public static final String JSON_SCHEMA_EXTENSION = ".json";
public static final String XML_SCHEMA_EXTENSION = ".xsd";
public static final String JSON = "json";
public static final String XML = "xml";
private boolean schemasEnabled;
private String schemasFolderPath;
public DefinitionsDocument(Swagger swagger, MarkupLanguage markupLanguage, String schemasFolderPath){
super(swagger, markupLanguage);
if(StringUtils.isNotBlank(schemasFolderPath)){
this.schemasEnabled = true;
this.schemasFolderPath = schemasFolderPath;
}
if(schemasEnabled){
if (logger.isDebugEnabled()) {
logger.debug("Include schemas is enabled.");
}
}else{
if (logger.isDebugEnabled()) {
logger.debug("Include schemas is disabled.");
}
}
}
@Override
public MarkupDocument build() throws IOException {
definitions(swagger.getDefinitions());
return this;
}
/**
* Builds the Swagger definitions.
*
* @param definitions the Swagger definitions
*/
private void definitions(Map<String, Model> definitions) throws IOException {
if(MapUtils.isNotEmpty(definitions)){
this.documentBuilder.sectionTitleLevel1(DEFINITIONS);
for(Map.Entry<String, Model> definitionsEntry : definitions.entrySet()){
String definitionName = definitionsEntry.getKey();
if(StringUtils.isNotBlank(definitionName)) {
if (checkThatDefinitionIsNotInIgnoreList(definitionName)) {
definition(definitionName, definitionsEntry.getValue());
definitionSchema(definitionName);
if (logger.isInfoEnabled()) {
logger.info("Definition processed: {}", definitionName);
}
}else{
if (logger.isDebugEnabled()) {
logger.debug("Definition was ignored: {}", definitionName);
}
}
}
}
}
}
/**
* Checks that the definition is not in the list of ignored definitions.
*
* @param definitionName the name of the definition
* @return true if the definition can be processed
*/
private boolean checkThatDefinitionIsNotInIgnoreList(String definitionName) {
return !IGNORED_DEFINITIONS.contains(definitionName);
}
/**
* Builds a concrete definition
*
* @param definitionName the name of the definition
* @param model the Swagger Model of the definition
*/
private void definition(String definitionName, Model model) {
this.documentBuilder.sectionTitleLevel2(definitionName);
Map<String, Property> properties = model.getProperties();
List<String> csvContent = new ArrayList<>();
csvContent.add(NAME_COLUMN + DELIMITER + TYPE_COLUMN + DELIMITER + REQUIRED_COLUMN);
for (Map.Entry<String, Property> propertyEntry : properties.entrySet()) {
Property property = propertyEntry.getValue();
csvContent.add(propertyEntry.getKey() + DELIMITER + property.getType() + DELIMITER + property.getRequired());
}
this.documentBuilder.tableWithHeaderRow(csvContent);
}
private void definitionSchema(String definitionName) throws IOException {
if(schemasEnabled) {
if (StringUtils.isNotBlank(definitionName)) {
schema(JSON_SCHEMA, schemasFolderPath, definitionName + JSON_SCHEMA_EXTENSION, JSON);
schema(XML_SCHEMA, schemasFolderPath, definitionName + XML_SCHEMA_EXTENSION, XML);
}
}
}
private void schema(String title, String schemasFolderPath, String schemaName, String language) throws IOException {
java.nio.file.Path path = Paths.get(schemasFolderPath, schemaName);
if (Files.isReadable(path)) {
this.documentBuilder.sectionTitleLevel3(title);
this.documentBuilder.source(FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim(), language);
if (logger.isInfoEnabled()) {
logger.info("Schema file processed: {}", path);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Schema file is not readable: {}", path);
}
}
}
}

View File

@@ -0,0 +1,61 @@
package io.github.robwin.swagger2markup.builder.document;
import com.wordnik.swagger.models.Swagger;
import io.github.robwin.swagger2markup.builder.markup.DocumentBuilder;
import io.github.robwin.swagger2markup.builder.markup.DocumentBuilders;
import io.github.robwin.swagger2markup.builder.markup.MarkupLanguage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* Project: swagger2markup
* Copyright: Deutsche Telekom AG
*
* @author Robert Winkler <robert.winkler@telekom.de>
* @since 2.0.0
*/
public abstract class MarkupDocument {
protected static final String DELIMITER = ",";
protected static final String REQUIRED_COLUMN = "Required";
protected Logger logger = LoggerFactory.getLogger(getClass());
protected Swagger swagger;
protected MarkupLanguage markupLanguage;
protected DocumentBuilder documentBuilder;
MarkupDocument(Swagger swagger, MarkupLanguage markupLanguage){
this.swagger = swagger;
this.markupLanguage = markupLanguage;
this.documentBuilder = DocumentBuilders.documentBuilder(markupLanguage);
}
/**
* Builds the MarkupDocument.
*
* @return the built MarkupDocument
* @throws IOException if the files to include are not readable
*/
public abstract MarkupDocument build() throws IOException ;
/**
* Returns a string representation of the document.
*/
public String toString(){
return documentBuilder.toString();
}
/**
* Writes the content of the builder to a file and clears the builder.
*
* @param directory the directory where the generated file should be stored
* @param fileName the name of the file
* @param charset the the charset to use for encoding
* @throws IOException if the file cannot be written
*/
public void writeToFile(String directory, String fileName, Charset charset) throws IOException{
documentBuilder.writeToFile(directory, fileName, charset);
}
}

View File

@@ -0,0 +1,233 @@
package io.github.robwin.swagger2markup.builder.document;
import com.wordnik.swagger.models.*;
import com.wordnik.swagger.models.parameters.Parameter;
import io.github.robwin.swagger2markup.builder.markup.MarkupLanguage;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Project: swagger2markup
* Copyright: Deutsche Telekom AG
*
* @author Robert Winkler <robert.winkler@telekom.de>
* @since 2.0.0
*/
public class PathsDocument extends MarkupDocument {
private static final String VERSION = "Version: ";
private static final String DESCRIPTION_COLUMN = "Description";
private static final String DESCRIPTION = DESCRIPTION_COLUMN;
private static final String PARAMETERS = "Parameters";
private static final String PRODUCES = "Produces";
private static final String CONSUMES = "Consumes";
private static final String RESPONSES = "Responses";
private static final String EXAMPLE_REQUEST = "Example request";
private static final String EXAMPLE_RESPONSE = "Example response";
private static final String NAME_COLUMN = "Name";
private static final String LOCATED_IN_COLUMN = "Located in";
private static final String CODE_COLUMN = "Code";
public static final String REQUEST_EXAMPLE_FILE_NAME = "request";
public static final String RESPONSE_EXAMPLE_FILE_NAME = "response";
private boolean examplesEnabled;
private String examplesFolderPath;
public PathsDocument(Swagger swagger, MarkupLanguage markupLanguage, String examplesFolderPath){
super(swagger, markupLanguage);
if(StringUtils.isNotBlank(examplesFolderPath)){
this.examplesEnabled = true;
this.examplesFolderPath = examplesFolderPath;
}
if(examplesEnabled){
if (logger.isDebugEnabled()) {
logger.debug("Include examples is enabled.");
}
}else{
if (logger.isDebugEnabled()) {
logger.debug("Include examples is disabled.");
}
}
}
@Override
public MarkupDocument build() throws IOException {
documentHeader(swagger.getInfo());
paths(swagger.getPaths());
return this;
}
/**
* Builds the document header
*
* @param info the Swagger Info
*/
private void documentHeader(Info info) {
this.documentBuilder
.documentTitle(info.getTitle())
.textLine(info.getDescription())
.textLine(VERSION + info.getVersion())
.newLine();
}
/**
* Builds all paths of the Swagger file
*
* @param paths a Map of Swagger Paths
*/
private void paths(Map<String, Path> paths) throws IOException {
if(MapUtils.isNotEmpty(paths)) {
//this.documentBuilder.sectionTitleLevel1(FEATURES);
for (Map.Entry<String, Path> entry : paths.entrySet()) {
Path path = entry.getValue();
path("GET", entry.getKey(), path.getGet());
path("PUT", entry.getKey(), path.getPut());
path("DELETE", entry.getKey(), path.getDelete());
path("POST", entry.getKey(), path.getPost());
path("PATCH", entry.getKey(), path.getPatch());
}
}
}
/**
* Builds a path
*
* @param httpMethod the HTTP method of the path
* @param resourcePath the URL of the path
* @param operation the Swagger Operation
*/
private void path(String httpMethod, String resourcePath, Operation operation) throws IOException {
if(operation != null){
pathTitle(httpMethod, resourcePath, operation);
descriptionSection(operation);
parametersSection(operation);
responsesSection(operation);
consumesSection(operation);
producesSection(operation);
examplesSection(operation);
}
}
private void pathTitle(String httpMethod, String resourcePath, Operation operation) {
String summary = operation.getSummary();
String title;
if(StringUtils.isNotBlank(summary)) {
title = summary;
this.documentBuilder.sectionTitleLevel1(title);
this.documentBuilder.listing(httpMethod + " " + resourcePath);
}else{
title = httpMethod + " " + resourcePath;
this.documentBuilder.sectionTitleLevel1(title);
}
if (logger.isInfoEnabled()) {
logger.info("Path processed: {}", title);
}
}
private void descriptionSection(Operation operation) {
String description = operation.getDescription();
if(StringUtils.isNotBlank(description)){
this.documentBuilder.sectionTitleLevel2(DESCRIPTION);
this.documentBuilder.paragraph(description);
}
}
private void parametersSection(Operation operation) {
List<Parameter> parameters = operation.getParameters();
if(CollectionUtils.isNotEmpty(parameters)){
List<String> csvContent = new ArrayList<>();
csvContent.add(NAME_COLUMN + DELIMITER + LOCATED_IN_COLUMN + DELIMITER + DESCRIPTION_COLUMN + DELIMITER + REQUIRED_COLUMN);
for(Parameter parameter : parameters){
csvContent.add(parameter.getName() + DELIMITER + parameter.getIn() + DELIMITER + parameter.getDescription() + DELIMITER + parameter.getRequired());
}
this.documentBuilder.sectionTitleLevel2(PARAMETERS);
this.documentBuilder.tableWithHeaderRow(csvContent);
}
}
private void consumesSection(Operation operation) {
List<String> consumes = operation.getConsumes();
if(CollectionUtils.isNotEmpty(consumes)){
this.documentBuilder.sectionTitleLevel2(CONSUMES);
this.documentBuilder.unorderedList(consumes);
}
}
private void producesSection(Operation operation) {
List<String> produces = operation.getProduces();
if(CollectionUtils.isNotEmpty(produces)){
this.documentBuilder.sectionTitleLevel2(PRODUCES);
this.documentBuilder.unorderedList(produces);
}
}
/**
* Builds the example section of a Swagger Operation
*
* @param operation the Swagger Operation
* @throws IOException if the example file is not readable
*/
private void examplesSection(Operation operation) throws IOException {
if(examplesEnabled){
String summary = operation.getSummary();
if(StringUtils.isNotBlank(summary)) {
String exampleFolder = summary.replace(".", "").replace(" ", "_").toLowerCase();
example(EXAMPLE_REQUEST, exampleFolder, REQUEST_EXAMPLE_FILE_NAME);
example(EXAMPLE_RESPONSE, exampleFolder, RESPONSE_EXAMPLE_FILE_NAME);
}
}
}
/**
* Builds a concrete example
*
* @param title the title of the example
* @param exampleFolder the name of the folder where the example file resides
* @param exampleFileName the name of the example file
* @throws IOException
*/
private void example(String title, String exampleFolder, String exampleFileName) throws IOException {
for (String fileNameExtension : markupLanguage.getFileNameExtensions()) {
java.nio.file.Path path = Paths.get(examplesFolderPath, exampleFolder, exampleFileName + fileNameExtension);
if (Files.isReadable(path)) {
this.documentBuilder.sectionTitleLevel2(title);
this.documentBuilder.paragraph(FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim());
if (logger.isInfoEnabled()) {
logger.info("Example file processed: {}", path);
}
break;
} else {
if (logger.isDebugEnabled()) {
logger.debug("Example file is not readable: {}", path);
}
}
}
}
private void responsesSection(Operation operation) {
Map<String, Response> responses = operation.getResponses();
if(MapUtils.isNotEmpty(responses)){
List<String> csvContent = new ArrayList<>();
csvContent.add(CODE_COLUMN + DELIMITER + DESCRIPTION_COLUMN);
for(Map.Entry<String, Response> entry : responses.entrySet()){
Response response = entry.getValue();
csvContent.add(entry.getKey() + DELIMITER + response.getDescription());
}
this.documentBuilder.sectionTitleLevel2(RESPONSES);
this.documentBuilder.tableWithHeaderRow(csvContent);
}
}
}

View File

@@ -1,5 +1,13 @@
package io.swagger2markup.builder; package io.github.robwin.swagger2markup.builder.markup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
/** /**
@@ -9,6 +17,7 @@ public abstract class AbstractDocumentBuilder implements DocumentBuilder {
protected StringBuilder documentBuilder = new StringBuilder(); protected StringBuilder documentBuilder = new StringBuilder();
protected String newLine = System.getProperty("line.separator"); protected String newLine = System.getProperty("line.separator");
protected Logger logger = LoggerFactory.getLogger(getClass());
protected void documentTitle(Markup markup, String title){ protected void documentTitle(Markup markup, String title){
documentBuilder.append(markup).append(title).append(newLine); documentBuilder.append(markup).append(title).append(newLine);
@@ -26,6 +35,7 @@ public abstract class AbstractDocumentBuilder implements DocumentBuilder {
documentBuilder.append(markup).append(title).append(newLine); documentBuilder.append(markup).append(title).append(newLine);
} }
@Override
public DocumentBuilder textLine(String text){ public DocumentBuilder textLine(String text){
documentBuilder.append(text).append(newLine); documentBuilder.append(text).append(newLine);
return this; return this;
@@ -39,7 +49,7 @@ public abstract class AbstractDocumentBuilder implements DocumentBuilder {
delimitedTextLine(markup, text); delimitedTextLine(markup, text);
} }
private void delimitedTextLine(Markup markup, String text){ protected void delimitedTextLine(Markup markup, String text){
documentBuilder.append(markup).append(newLine).append(text).append(newLine).append(markup).append(newLine).append(newLine); documentBuilder.append(markup).append(newLine).append(text).append(newLine).append(markup).append(newLine).append(newLine);
} }
@@ -62,6 +72,7 @@ public abstract class AbstractDocumentBuilder implements DocumentBuilder {
documentBuilder.append(newLine); documentBuilder.append(newLine);
} }
@Override
public DocumentBuilder newLine(){ public DocumentBuilder newLine(){
documentBuilder.append(newLine); documentBuilder.append(newLine);
return this; return this;
@@ -71,4 +82,16 @@ public abstract class AbstractDocumentBuilder implements DocumentBuilder {
public String toString(){ public String toString(){
return documentBuilder.toString(); return documentBuilder.toString();
} }
@Override
public void writeToFile(String directory, String fileNameWithExtension, Charset charset) throws IOException {
Files.createDirectories(Paths.get(directory));
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(directory, fileNameWithExtension), charset)){
writer.write(documentBuilder.toString());
}
if (logger.isInfoEnabled()) {
logger.info("{} was written to: {}", fileNameWithExtension, directory);
}
documentBuilder = new StringBuilder();
}
} }

View File

@@ -1,5 +1,7 @@
package io.swagger2markup.builder; package io.github.robwin.swagger2markup.builder.markup;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List; import java.util.List;
/** /**
@@ -20,6 +22,8 @@ public interface DocumentBuilder {
DocumentBuilder listing(String text); DocumentBuilder listing(String text);
DocumentBuilder source(String text, String language);
DocumentBuilder boldTextLine(String text); DocumentBuilder boldTextLine(String text);
DocumentBuilder italicTextLine(String text); DocumentBuilder italicTextLine(String text);
@@ -30,5 +34,18 @@ public interface DocumentBuilder {
DocumentBuilder newLine(); DocumentBuilder newLine();
/**
* Returns a string representation of the document.
*/
String toString(); String toString();
/**
* Writes the content of the builder to a file and clears the builder.
*
* @param directory the directory where the generated file should be stored
* @param fileName the name of the file
* @param charset the the charset to use for encoding
* @throws IOException if the file cannot be written
*/
void writeToFile(String directory, String fileName, Charset charset) throws IOException;
} }

View File

@@ -0,0 +1,25 @@
package io.github.robwin.swagger2markup.builder.markup;
import io.github.robwin.swagger2markup.builder.markup.asciidoc.AsciiDocBuilder;
import io.github.robwin.swagger2markup.builder.markup.markdown.MarkdownBuilder;
/**
* Project: swagger2markup
* Copyright: Deutsche Telekom AG
*
* @author Robert Winkler <robert.winkler@telekom.de>
* @since 2.0.0
*/
public final class DocumentBuilders {
private DocumentBuilders(){};
public static DocumentBuilder documentBuilder(MarkupLanguage markupLanguage){
switch(markupLanguage){
case MARKDOWN: return new MarkdownBuilder();
case ASCIIDOC: return new AsciiDocBuilder();
default: return new AsciiDocBuilder();
}
}
}

View File

@@ -1,4 +1,4 @@
package io.swagger2markup.builder; package io.github.robwin.swagger2markup.builder.markup;
/** /**
* Project: swagger2asciidoc * Project: swagger2asciidoc

View File

@@ -0,0 +1,25 @@
package io.github.robwin.swagger2markup.builder.markup;
import java.util.Arrays;
import java.util.List;
/**
* @author Robert Winkler
*/
public enum MarkupLanguage {
ASCIIDOC(".adoc,.asciidoc"),
MARKDOWN(".md,.markdown");
private final String fileNameExtensions;
/**
* @param fileNameExtensions file name suffix
*/
private MarkupLanguage(final String fileNameExtensions) {
this.fileNameExtensions = fileNameExtensions;
}
public List<String> getFileNameExtensions() {
return Arrays.asList(fileNameExtensions.split(","));
}
}

View File

@@ -1,6 +1,6 @@
package io.swagger2markup.builder.asciidoc; package io.github.robwin.swagger2markup.builder.markup.asciidoc;
import io.swagger2markup.builder.Markup; import io.github.robwin.swagger2markup.builder.markup.Markup;
/** /**
* @author Robert Winkler * @author Robert Winkler

View File

@@ -1,8 +1,10 @@
package io.swagger2markup.builder.asciidoc; package io.github.robwin.swagger2markup.builder.markup.asciidoc;
import io.swagger2markup.builder.AbstractDocumentBuilder; import io.github.robwin.swagger2markup.builder.markup.AbstractDocumentBuilder;
import io.swagger2markup.builder.DocumentBuilder; import io.github.robwin.swagger2markup.builder.markup.DocumentBuilder;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List; import java.util.List;
/** /**
@@ -64,6 +66,13 @@ public class AsciiDocBuilder extends AbstractDocumentBuilder{
return this; return this;
} }
@Override
public DocumentBuilder source(String text, String language){
documentBuilder.append(String.format("[source,%s]", language)).append(newLine);
listing(AsciiDoc.LISTING, text);
return this;
}
@Override @Override
public DocumentBuilder tableWithHeaderRow(List<String> rowsInCSV){ public DocumentBuilder tableWithHeaderRow(List<String> rowsInCSV){
documentBuilder.append("[format=\"csv\", options=\"header\"]").append(newLine); documentBuilder.append("[format=\"csv\", options=\"header\"]").append(newLine);
@@ -74,4 +83,11 @@ public class AsciiDocBuilder extends AbstractDocumentBuilder{
documentBuilder.append(AsciiDoc.TABLE).append(newLine).append(newLine); documentBuilder.append(AsciiDoc.TABLE).append(newLine).append(newLine);
return this; return this;
} }
@Override
public void writeToFile(String directory, String fileName, Charset charset) throws IOException {
String fileNameWithExtension = fileName + ".adoc";
super.writeToFile(directory, fileNameWithExtension, charset);
}
} }

View File

@@ -1,6 +1,6 @@
package io.swagger2markup.builder.markdown; package io.github.robwin.swagger2markup.builder.markup.markdown;
import io.swagger2markup.builder.Markup; import io.github.robwin.swagger2markup.builder.markup.Markup;
/** /**
* Project: swagger2asciidoc * Project: swagger2asciidoc

View File

@@ -1,8 +1,10 @@
package io.swagger2markup.builder.markdown; package io.github.robwin.swagger2markup.builder.markup.markdown;
import io.swagger2markup.builder.AbstractDocumentBuilder; import io.github.robwin.swagger2markup.builder.markup.AbstractDocumentBuilder;
import io.swagger2markup.builder.DocumentBuilder; import io.github.robwin.swagger2markup.builder.markup.DocumentBuilder;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -51,6 +53,14 @@ public class MarkdownBuilder extends AbstractDocumentBuilder
return this; return this;
} }
@Override
public DocumentBuilder source(String text, String language){
documentBuilder.append(Markdown.LISTING).append(language).append(newLine).
append(text).append(newLine).
append(Markdown.LISTING).append(newLine).append(newLine);
return this;
}
@Override @Override
public DocumentBuilder boldTextLine(String text){ public DocumentBuilder boldTextLine(String text){
boldTextLine(Markdown.BOLD, text); boldTextLine(Markdown.BOLD, text);
@@ -101,4 +111,10 @@ public class MarkdownBuilder extends AbstractDocumentBuilder
newLine().newLine(); newLine().newLine();
return this; return this;
} }
@Override
public void writeToFile(String directory, String fileName, Charset charset) throws IOException {
String fileNameWithExtension = fileName + ".md";
super.writeToFile(directory, fileNameWithExtension, charset);
}
} }

View File

@@ -1,203 +0,0 @@
package io.swagger2markup;
import com.wordnik.swagger.models.*;
import com.wordnik.swagger.models.parameters.Parameter;
import com.wordnik.swagger.models.properties.Property;
import io.swagger.parser.SwaggerParser;
import io.swagger2markup.builder.DocumentBuilder;
import io.swagger2markup.builder.asciidoc.AsciiDocBuilder;
import io.swagger2markup.builder.markdown.MarkdownBuilder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author Robert Winkler
*/
public class Swagger2MarkupConverter {
private static final Logger logger = LoggerFactory.getLogger(Swagger2MarkupConverter.class);
private static final String VERSION = "Version: ";
private static final String SUMMARY = "Summary";
private static final String DESCRIPTION = "Description";
private static final String PARAMETERS = "Parameters";
private static final String PRODUCES = "Produces";
private static final String CONSUMES = "Consumes";
private static final String RESPONSES = "Responses";
private static final String DEFINITIONS = "Definitions";
private static final List<String> IGNORED_DEFINITIONS = Arrays.asList("Void");
private final Swagger swagger;
private DocumentBuilder documentBuilder;
private Swagger2MarkupConverter(String swaggerFileLocation){
swagger = new SwaggerParser().read(swaggerFileLocation);
}
public static Swagger2MarkupConverter from(String swaggerFileLocation){
return new Swagger2MarkupConverter(swaggerFileLocation);
}
public void toAsciiDoc(String fileLocation) throws IOException {
documentBuilder = new AsciiDocBuilder();
buildDocument();
writeAsciiDocFile(fileLocation);
}
public void toMarkdown(String fileLocation) throws IOException {
documentBuilder = new MarkdownBuilder();
buildDocument();
writeAsciiDocFile(fileLocation);
}
private void buildDocument(){
documentHeader(swagger.getInfo());
paths(swagger.getPaths());
definitions(swagger.getDefinitions());
}
private void writeAsciiDocFile(String asciiDocFileLocation) throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(asciiDocFileLocation),
StandardCharsets.UTF_8)){
writer.write(documentBuilder.toString());
} catch (IOException e) {
logger.warn("Failed to convert Swagger file to AsciiDoc", e);
throw e;
}
}
private void paths(Map<String, Path> paths) {
for(Map.Entry<String, Path> entry : paths.entrySet()){
Path path = entry.getValue();
path("GET", entry.getKey(), path.getGet());
path("PUT", entry.getKey(), path.getPut());
path("DELETE", entry.getKey(), path.getDelete());
path("POST", entry.getKey(), path.getPost());
path("PATCH", entry.getKey(), path.getPatch());
}
}
private void documentHeader(Info info) {
documentBuilder
.documentTitle(info.getTitle())
.textLine(info.getDescription())
.textLine(VERSION + info.getVersion())
.newLine();
}
private void path(String httpMethod, String resourcePath, Operation operation) {
if(operation != null){
pathTitle(httpMethod, resourcePath, operation);
descriptionSection(operation);
parametersSection(operation);
responsesSection(operation);
consumesSection(operation);
producesSection(operation);
}
}
private void pathTitle(String httpMethod, String resourcePath, Operation operation) {
String summary = operation.getSummary();
if(StringUtils.isNotBlank(summary)) {
documentBuilder.sectionTitleLevel1(operation.getSummary());
documentBuilder.listing(httpMethod + " " + resourcePath);
}else{
documentBuilder.sectionTitleLevel1(httpMethod + " " + resourcePath);
}
}
private void descriptionSection(Operation operation) {
String description = operation.getDescription();
if(StringUtils.isNotBlank(description)){
documentBuilder.sectionTitleLevel2(DESCRIPTION);
documentBuilder.paragraph(description);
}
}
private void consumesSection(Operation operation) {
List<String> consumes = operation.getConsumes();
if(CollectionUtils.isNotEmpty(consumes)){
documentBuilder.sectionTitleLevel2(CONSUMES);
documentBuilder.unorderedList(consumes);
}
}
private void producesSection(Operation operation) {
List<String> produces = operation.getProduces();
if(CollectionUtils.isNotEmpty(produces)){
documentBuilder.sectionTitleLevel2(PRODUCES);
documentBuilder.unorderedList(produces);
}
}
private void parametersSection(Operation operation) {
List<Parameter> parameters = operation.getParameters();
if(CollectionUtils.isNotEmpty(parameters)){
List<String> csvContent = new ArrayList<>();
csvContent.add("Name,Located in,Description,Required");
for(Parameter parameter : parameters){
StringBuilder rowBuilder = new StringBuilder();
rowBuilder.append(parameter.getName()).append(",").
append(parameter.getIn()).append(",").
append(parameter.getDescription()).append(",").
append(parameter.getRequired());
csvContent.add(rowBuilder.toString());
}
documentBuilder.sectionTitleLevel2(PARAMETERS);
documentBuilder.tableWithHeaderRow(csvContent);
}
}
private void responsesSection(Operation operation) {
Map<String, Response> responses = operation.getResponses();
if(MapUtils.isNotEmpty(responses)){
List<String> csvContent = new ArrayList<>();
csvContent.add("Code,Description");
for(Map.Entry<String, Response> entry : responses.entrySet()){
Response response = entry.getValue();
StringBuilder rowBuilder = new StringBuilder();
rowBuilder.append(entry.getKey()).append(",").
append(response.getDescription());
csvContent.add(rowBuilder.toString());
}
documentBuilder.sectionTitleLevel2(RESPONSES);
documentBuilder.tableWithHeaderRow(csvContent);
}
}
private void definitions(Map<String, Model> definitions) {
if(MapUtils.isNotEmpty(definitions)){
documentBuilder.sectionTitleLevel1(DEFINITIONS);
for(Map.Entry<String, Model> definitionsEntry : definitions.entrySet()){
String definitionName = definitionsEntry.getKey();
if(!IGNORED_DEFINITIONS.contains(definitionName)) {
documentBuilder.sectionTitleLevel2(definitionName);
Model model = definitionsEntry.getValue();
Map<String, Property> properties = model.getProperties();
List<String> csvContent = new ArrayList<>();
csvContent.add("Name,Type,Required");
for (Map.Entry<String, Property> propertyEntry : properties.entrySet()) {
Property property = propertyEntry.getValue();
StringBuilder rowBuilder = new StringBuilder();
rowBuilder.append(propertyEntry.getKey()).append(",").
append(property.getType()).append(",").append(property.getRequired());
csvContent.add(rowBuilder.toString());
}
documentBuilder.tableWithHeaderRow(csvContent);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

View File

@@ -0,0 +1,28 @@
package io.github.robwin.swagger2markup;
import io.github.robwin.swagger2markup.builder.markup.MarkupLanguage;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
/**
* @author Robert Winkler
*/
public class Swagger2MarkupConverterTest {
@Test
public void testSwagger2AsciiDocConverter() throws IOException {
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger.json").getFile());
Swagger2MarkupConverter.from(file.getAbsolutePath()).
withMarkupLanguage(MarkupLanguage.MARKDOWN).
withExamples("docs").withSchemas("docs/schemas").build()
.intoFolder("src/docs/markdown");
Swagger2MarkupConverter.from(file.getAbsolutePath()).
withExamples("docs").withSchemas("docs/schemas").build()
.intoFolder("src/docs/asciidoc");
}
}

View File

@@ -0,0 +1,26 @@
package io.github.robwin.swagger2markup.builder;
import io.github.robwin.swagger2markup.builder.markup.DocumentBuilder;
import io.github.robwin.swagger2markup.builder.markup.DocumentBuilders;
import io.github.robwin.swagger2markup.builder.markup.MarkupLanguage;
import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* Project: swagger2markup
* Copyright: Deutsche Telekom AG
*
* @author Robert Winkler <robert.winkler@telekom.de>
* @since 2.0.0
*/
public class DocumentBuilderTest {
@Test
public void testToFile() throws IOException {
DocumentBuilder builder = DocumentBuilders.documentBuilder(MarkupLanguage.ASCIIDOC);
builder.documentTitle("Test title").textLine("Text line").writeToFile("/tmp", "test.adoc", StandardCharsets.UTF_8);
}
}

View File

@@ -1,20 +0,0 @@
package io.swagger2markup;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
/**
* @author Robert Winkler
*/
public class Swagger2MarkupConverterTest {
@Test
public void testSwagger2AsciiDocConverter() throws IOException {
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger.json").getFile());
Swagger2MarkupConverter.from(file.getAbsolutePath()).toAsciiDoc("src/docs/asciidoc/swagger.adoc");
Swagger2MarkupConverter.from(file.getAbsolutePath()).toMarkdown("src/docs/markdown/swagger.md");
}
}