Compare commits

...

4 Commits

Author SHA1 Message Date
Robert Winkler
eb418c07f5 Updated publishing gradle script 2015-11-12 14:37:50 +01:00
Robert Winkler
9a265fbbc3 Updated RELEASENOTES 2015-11-12 13:12:15 +01:00
Robert Winkler
cf32d6cf29 [New Feature] Added support to group the paths by tags or as-is
[New Feature] Added support to order the definitions by natural ordering or as-is
2015-11-12 13:10:33 +01:00
Robert Winkler
9d216e3dc2 [Bug] Fixed unit test issue with composed models 2015-11-09 15:49:35 +01:00
20 changed files with 1764 additions and 252 deletions

View File

@@ -65,6 +65,10 @@
* Enhancement #26 and #27: Added a pre-process hook to modify a Swagger Model before it is converted.
* Bugfix #29: Tags are rendered twice
=== Version 0.9.0
== Version 0.9.0
* Updated swagger-parser from v1.0.8 to v1.0.13
* Support for global responses and parameters
* Support for global responses and parameters
=== Version 0.9.1
* Added support to group the paths by tags or as-is
* Added support to order the definitions by natural ordering or as-is

View File

@@ -13,7 +13,7 @@ buildscript {
}
}
description = 'swagger2markup Build'
version = '0.9.0'
version = '0.9.1'
group = 'io.github.robwin'
apply plugin: 'java'
@@ -53,14 +53,14 @@ dependencies {
dependencyManagement {
dependencies {
dependency "io.github.robwin:markup-document-builder:0.1.4"
dependency "io.github.robwin:markup-document-builder:0.1.5"
dependency "io.swagger:swagger-compat-spec-parser:1.0.13"
dependency "commons-collections:commons-collections:3.2.1"
dependency "commons-io:commons-io:2.4"
dependency "junit:junit:4.11"
dependency "org.slf4j:slf4j-api:1.7.12"
dependency "ch.qos.logback:logback-classic:1.1.2"
dependency "org.assertj:assertj-core:2.0.0"
dependency "org.assertj:assertj-core:2.2.0"
}
}

View File

@@ -38,9 +38,9 @@ bintray {
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'
websiteUrl = 'https://github.com/Swagger2Markup/swagger2markup'
issueTrackerUrl = 'https://github.com/Swagger2Markup/swagger2markup/issues'
vcsUrl = 'https://github.com/Swagger2Markup/swagger2markup.git'
desc = 'A Swagger to Markup (AsciiDoc and Markdown) converter.'
licenses = ['Apache-2.0']
version {
@@ -76,15 +76,15 @@ publishing {
root.appendNode('name', 'swagger2markup')
root.appendNode('packaging', 'jar')
root.appendNode('url', 'https://github.com/RobWin/swagger2markup')
root.appendNode('url', 'https://github.com/Swagger2Markup/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('url', 'https://github.com/Swagger2Markup/swagger2markup/blob/master/LICENSE.txt')
license.appendNode('distribution', 'repo')
root.appendNode('scm').appendNode('url', 'https://github.com/RobWin/swagger2markup.git')
root.appendNode('scm').appendNode('url', 'https://github.com/Swagger2Markup/swagger2markup.git')
def developers = root.appendNode('developers')
devs.each {

View File

@@ -0,0 +1,24 @@
/*
*
* 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.
*
*
*/
package io.github.robwin.swagger2markup;
public enum GroupBy {
AS_IS,
TAGS
}

View File

@@ -0,0 +1,23 @@
/*
*
* 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.
*
*
*/
package io.github.robwin.swagger2markup;
public enum OrderBy {
AS_IS,
NATURAL
}

View File

@@ -17,16 +17,12 @@
*
*/
package io.github.robwin.swagger2markup;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.github.robwin.swagger2markup.config.Swagger2MarkupConfig;
import io.github.robwin.swagger2markup.builder.document.*;
import io.github.robwin.swagger2markup.utils.Consumer;
import io.swagger.models.Swagger;
import io.swagger.parser.SwaggerParser;
import io.swagger.util.Json;
import io.swagger.util.Yaml;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,31 +36,16 @@ import java.nio.charset.StandardCharsets;
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 final String descriptionsFolderPath;
private final boolean separatedDefinitions;
private final Swagger2MarkupConfig swagger2MarkupConfig;
private static final String OVERVIEW_DOCUMENT = "overview";
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
* @param descriptionsFolderPath the folderPath where descriptions are stored
* @param separatedDefinitions create separate definition files for each model definition.
* @param swagger2MarkupConfig the configuration
*/
Swagger2MarkupConverter(MarkupLanguage markupLanguage, Swagger swagger, String examplesFolderPath, String schemasFolderPath, String descriptionsFolderPath, boolean separatedDefinitions){
this.markupLanguage = markupLanguage;
this.swagger = swagger;
this.examplesFolderPath = examplesFolderPath;
this.schemasFolderPath = schemasFolderPath;
this.descriptionsFolderPath = descriptionsFolderPath;
this.separatedDefinitions = separatedDefinitions;
Swagger2MarkupConverter(Swagger2MarkupConfig swagger2MarkupConfig){
this.swagger2MarkupConfig = swagger2MarkupConfig;
}
/**
@@ -85,33 +66,24 @@ public class Swagger2MarkupConverter {
* @return a Swagger2MarkupConverter
*/
public static Builder from(Swagger swagger){
Validate.notNull(swagger, "swagger must not be null!");
Validate.notNull(swagger, "Swagger must not be null!");
return new Builder(swagger);
}
/**
* Creates a Swagger2MarkupConverter.Builder from a given Swagger YAML or JSON String.
*
* @param swagger the Swagger YAML or JSON String.
* @param swaggerAsString the Swagger YAML or JSON String.
* @return a Swagger2MarkupConverter
* @throws java.io.IOException if String can not be parsed
*/
public static Builder fromString(String swagger) throws IOException {
Validate.notEmpty(swagger, "swagger must not be null!");
ObjectMapper mapper;
if(swagger.trim().startsWith("{")) {
mapper = Json.mapper();
}else {
mapper = Yaml.mapper();
}
JsonNode rootNode = mapper.readTree(swagger);
// must have swagger node set
JsonNode swaggerNode = rootNode.get("swagger");
if(swaggerNode == null)
public static Builder fromString(String swaggerAsString) throws IOException {
Validate.notEmpty(swaggerAsString, "Swagger String must not be null!");
Swagger swagger = new SwaggerParser().parse(swaggerAsString);
if(swagger == null)
throw new IllegalArgumentException("Swagger String is in the wrong format");
return new Builder(mapper.convertValue(rootNode, Swagger.class));
return new Builder(swagger);
}
/**
@@ -143,9 +115,9 @@ public class Swagger2MarkupConverter {
* @throws IOException if a file cannot be written
*/
private void buildDocuments(String directory) throws IOException {
new OverviewDocument(swagger, markupLanguage).build().writeToFile(directory, OVERVIEW_DOCUMENT, StandardCharsets.UTF_8);
new PathsDocument(swagger, markupLanguage, examplesFolderPath, descriptionsFolderPath).build().writeToFile(directory, PATHS_DOCUMENT, StandardCharsets.UTF_8);
new DefinitionsDocument(swagger, markupLanguage, schemasFolderPath, descriptionsFolderPath, separatedDefinitions, directory).build().writeToFile(directory, DEFINITIONS_DOCUMENT, StandardCharsets.UTF_8);
new OverviewDocument(swagger2MarkupConfig).build().writeToFile(directory, OVERVIEW_DOCUMENT, StandardCharsets.UTF_8);
new PathsDocument(swagger2MarkupConfig).build().writeToFile(directory, PATHS_DOCUMENT, StandardCharsets.UTF_8);
new DefinitionsDocument(swagger2MarkupConfig, directory).build().writeToFile(directory, DEFINITIONS_DOCUMENT, StandardCharsets.UTF_8);
}
/**
@@ -153,10 +125,10 @@ public class Swagger2MarkupConverter {
* @return a the document as a String
*/
private String buildDocuments() throws IOException {
return new OverviewDocument(swagger, markupLanguage).build().toString()
.concat(new PathsDocument(swagger, markupLanguage, examplesFolderPath, schemasFolderPath).build().toString()
.concat(new DefinitionsDocument(swagger, markupLanguage, schemasFolderPath, schemasFolderPath, false, null).build().toString()));
private String buildDocuments() {
return new OverviewDocument(swagger2MarkupConfig).build().toString()
.concat(new PathsDocument(swagger2MarkupConfig).build().toString()
.concat(new DefinitionsDocument(swagger2MarkupConfig, null).build().toString()));
}
@@ -166,6 +138,8 @@ public class Swagger2MarkupConverter {
private String schemasFolderPath;
private String descriptionsFolderPath;
private boolean separatedDefinitions;
private GroupBy pathsGroupedBy = GroupBy.AS_IS;
private OrderBy definitionsOrderedBy = OrderBy.NATURAL;
private MarkupLanguage markupLanguage = MarkupLanguage.ASCIIDOC;
/**
@@ -190,7 +164,7 @@ public class Swagger2MarkupConverter {
}
public Swagger2MarkupConverter build(){
return new Swagger2MarkupConverter(markupLanguage, swagger, examplesFolderPath, schemasFolderPath, descriptionsFolderPath, separatedDefinitions);
return new Swagger2MarkupConverter(new Swagger2MarkupConfig(swagger, markupLanguage, examplesFolderPath, schemasFolderPath, descriptionsFolderPath, separatedDefinitions, pathsGroupedBy, definitionsOrderedBy));
}
/**
@@ -248,6 +222,7 @@ public class Swagger2MarkupConverter {
/**
* Customize the Swagger data in any useful way
*
* @param preProcessor function object to mutate the swagger object
* @return the Swagger2MarkupConverter.Builder
*/
@@ -255,6 +230,28 @@ public class Swagger2MarkupConverter {
preProcessor.accept(this.swagger);
return this;
}
/**
* Specifies if the paths should be grouped by tags or stay as-is.
*
* @param pathsGroupedBy the GroupBy enum
* @return the Swagger2MarkupConverter.Builder
*/
public Builder withPathsGroupedBy(GroupBy pathsGroupedBy) {
this.pathsGroupedBy = pathsGroupedBy;
return this;
}
/**
* Specifies if the definitions should be ordered by natural ordering or stay as-is.
*
* @param definitionsOrderedBy the OrderBy enum
* @return the Swagger2MarkupConverter.Builder
*/
public Builder withDefinitionsOrderedBy(OrderBy definitionsOrderedBy) {
this.definitionsOrderedBy = definitionsOrderedBy;
return this;
}
}
}

View File

@@ -21,16 +21,16 @@ package io.github.robwin.swagger2markup.builder.document;
import com.google.common.collect.ImmutableMap;
import io.github.robwin.markup.builder.MarkupDocBuilder;
import io.github.robwin.markup.builder.MarkupDocBuilders;
import io.github.robwin.swagger2markup.OrderBy;
import io.github.robwin.swagger2markup.config.Swagger2MarkupConfig;
import io.github.robwin.swagger2markup.utils.PropertyUtils;
import io.swagger.models.ComposedModel;
import io.swagger.models.Model;
import io.swagger.models.RefModel;
import io.swagger.models.Swagger;
import io.swagger.models.properties.Property;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.github.robwin.swagger2markup.utils.PropertyUtils;
import io.swagger.models.refs.RefFormat;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import java.io.IOException;
@@ -39,6 +39,8 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.*;
/**
* @author Robert Winkler
*/
@@ -59,16 +61,18 @@ public class DefinitionsDocument extends MarkupDocument {
private String descriptionsFolderPath;
private boolean separatedDefinitionsEnabled;
private String outputDirectory;
private final OrderBy definitionsOrderedBy;
public DefinitionsDocument(Swagger swagger, MarkupLanguage markupLanguage, String schemasFolderPath, String descriptionsFolderPath, boolean separatedDefinitionsEnabled, String outputDirectory){
super(swagger, markupLanguage);
if(StringUtils.isNotBlank(schemasFolderPath)){
public DefinitionsDocument(Swagger2MarkupConfig swagger2MarkupConfig, String outputDirectory){
super(swagger2MarkupConfig);
this.definitionsOrderedBy = swagger2MarkupConfig.getDefinitionsOrderedBy();
if(isNotBlank(swagger2MarkupConfig.getSchemasFolderPath())){
this.schemasEnabled = true;
this.schemasFolderPath = schemasFolderPath;
this.schemasFolderPath = swagger2MarkupConfig.getSchemasFolderPath();
}
if(StringUtils.isNotBlank(descriptionsFolderPath)){
if(isNotBlank(swagger2MarkupConfig.getDescriptionsFolderPath())){
this.handWrittenDescriptionsEnabled = true;
this.descriptionsFolderPath = descriptionsFolderPath + "/" + DEFINITIONS.toLowerCase();
this.descriptionsFolderPath = swagger2MarkupConfig.getDescriptionsFolderPath() + "/" + DEFINITIONS.toLowerCase();
}
if(schemasEnabled){
if (logger.isDebugEnabled()) {
@@ -88,7 +92,7 @@ public class DefinitionsDocument extends MarkupDocument {
logger.debug("Include hand-written descriptions is disabled.");
}
}
this.separatedDefinitionsEnabled = separatedDefinitionsEnabled;
this.separatedDefinitionsEnabled = swagger2MarkupConfig.isSeparatedDefinitions();
if(this.separatedDefinitionsEnabled){
if (logger.isDebugEnabled()) {
logger.debug("Create separated definition files is enabled.");
@@ -103,7 +107,7 @@ public class DefinitionsDocument extends MarkupDocument {
}
@Override
public MarkupDocument build() throws IOException {
public MarkupDocument build(){
definitions(swagger.getDefinitions(), this.markupDocBuilder);
return this;
}
@@ -114,20 +118,32 @@ public class DefinitionsDocument extends MarkupDocument {
* @param definitions the Swagger definitions
* @param docBuilder the doc builder to use for output
*/
private void definitions(Map<String, Model> definitions, MarkupDocBuilder docBuilder) throws IOException {
private void definitions(Map<String, Model> definitions, MarkupDocBuilder docBuilder){
if(MapUtils.isNotEmpty(definitions)){
docBuilder.sectionTitleLevel1(DEFINITIONS);
for(Map.Entry<String, Model> definitionsEntry : definitions.entrySet()){
String definitionName = definitionsEntry.getKey();
if(StringUtils.isNotBlank(definitionName)) {
Set<String> definitionNames;
if(definitionsOrderedBy.equals(OrderBy.AS_IS)){
definitionNames = definitions.keySet();
}else{
definitionNames = new TreeSet<>(definitions.keySet());
}
for(String definitionName : definitionNames){
Model model = definitions.get(definitionName);
if(isNotBlank(definitionName)) {
if (checkThatDefinitionIsNotInIgnoreList(definitionName)) {
definition(definitions, definitionName, definitionsEntry.getValue(), docBuilder);
definition(definitions, definitionName, model, docBuilder);
definitionSchema(definitionName, docBuilder);
if (separatedDefinitionsEnabled) {
MarkupDocBuilder defDocBuilder = MarkupDocBuilders.documentBuilder(markupLanguage);
definition(definitions, definitionName, definitionsEntry.getValue(), defDocBuilder);
definition(definitions, definitionName, model, defDocBuilder);
definitionSchema(definitionName, defDocBuilder);
defDocBuilder.writeToFile(outputDirectory, definitionName.toLowerCase(), StandardCharsets.UTF_8);
try {
defDocBuilder.writeToFile(outputDirectory, definitionName.toLowerCase(), StandardCharsets.UTF_8);
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn(String.format("Failed to write definition file: %s", definitionName), e);
}
}
if (logger.isInfoEnabled()) {
logger.info("Separate definition file produced: {}", definitionName);
}
@@ -162,17 +178,17 @@ public class DefinitionsDocument extends MarkupDocument {
* @param model the Swagger Model of the definition
* @param docBuilder the docbuilder do use for output
*/
private void definition(Map<String, Model> definitions, String definitionName, Model model, MarkupDocBuilder docBuilder) throws IOException {
private void definition(Map<String, Model> definitions, String definitionName, Model model, MarkupDocBuilder docBuilder){
docBuilder.sectionTitleLevel2(definitionName);
descriptionSection(definitionName, model, docBuilder);
propertiesSection(definitions, definitionName, model, docBuilder);
}
private void propertiesSection(Map<String, Model> definitions, String definitionName, Model model, MarkupDocBuilder docBuilder) throws IOException {
private void propertiesSection(Map<String, Model> definitions, String definitionName, Model model, MarkupDocBuilder docBuilder){
Map<String, Property> properties = getAllProperties(definitions, model);
List<String> headerAndContent = new ArrayList<>();
List<String> header = Arrays.asList(NAME_COLUMN, DESCRIPTION_COLUMN, REQUIRED_COLUMN, SCHEMA_COLUMN, DEFAULT_COLUMN);
headerAndContent.add(StringUtils.join(header, DELIMITER));
headerAndContent.add(join(header, DELIMITER));
if(MapUtils.isNotEmpty(properties)){
for (Map.Entry<String, Property> propertyEntry : properties.entrySet()) {
Property property = propertyEntry.getValue();
@@ -183,7 +199,7 @@ public class DefinitionsDocument extends MarkupDocument {
Boolean.toString(property.getRequired()),
PropertyUtils.getType(property, markupLanguage),
PropertyUtils.getDefaultValue(property));
headerAndContent.add(StringUtils.join(content, DELIMITER));
headerAndContent.add(join(content, DELIMITER));
}
docBuilder.tableWithHeaderRow(headerAndContent);
}
@@ -191,9 +207,15 @@ public class DefinitionsDocument extends MarkupDocument {
private Map<String, Property> getAllProperties(Map<String, Model> definitions, Model model) {
if(model instanceof RefModel) {
final String ref = model.getReference();
RefModel refModel = (RefModel)model;
String ref;
if(refModel.getRefFormat().equals(RefFormat.INTERNAL)){
ref = refModel.getSimpleRef();
}else{
ref = model.getReference();
}
return definitions.containsKey(ref)
? getAllProperties(definitions, definitions.get(model.getReference()))
? getAllProperties(definitions, definitions.get(ref))
: null;
}
if(model instanceof ComposedModel) {
@@ -214,10 +236,10 @@ public class DefinitionsDocument extends MarkupDocument {
}
}
private void descriptionSection(String definitionName, Model model, MarkupDocBuilder docBuilder) throws IOException {
private void descriptionSection(String definitionName, Model model, MarkupDocBuilder docBuilder){
if(handWrittenDescriptionsEnabled){
String description = handWrittenPathDescription(definitionName.toLowerCase(), DESCRIPTION_FILE_NAME);
if(StringUtils.isNotBlank(description)){
if(isNotBlank(description)){
docBuilder.paragraph(description);
}else{
if (logger.isInfoEnabled()) {
@@ -233,24 +255,24 @@ public class DefinitionsDocument extends MarkupDocument {
private void modelDescription(Model model, MarkupDocBuilder docBuilder) {
String description = model.getDescription();
if (StringUtils.isNotBlank(description)) {
if (isNotBlank(description)) {
docBuilder.paragraph(description);
}
}
private String propertyDescription(String definitionName, String propertyName, Property property) throws IOException {
private String propertyDescription(String definitionName, String propertyName, Property property) {
String description;
if(handWrittenDescriptionsEnabled){
description = handWrittenPathDescription(definitionName.toLowerCase() + "/" + propertyName.toLowerCase(), DESCRIPTION_FILE_NAME);
if(StringUtils.isBlank(description)) {
if(isBlank(description)) {
if (logger.isInfoEnabled()) {
logger.info("Hand-written description file cannot be read. Trying to use description from Swagger source.");
}
description = StringUtils.defaultString(property.getDescription());
description = defaultString(property.getDescription());
}
}
else{
description = StringUtils.defaultString(property.getDescription());
description = defaultString(property.getDescription());
}
return description;
}
@@ -262,16 +284,21 @@ public class DefinitionsDocument extends MarkupDocument {
* @param descriptionFolder the name of the folder where the description file resides
* @param descriptionFileName the name of the description file
* @return the content of the file
* @throws IOException
*/
private String handWrittenPathDescription(String descriptionFolder, String descriptionFileName) throws IOException {
private String handWrittenPathDescription(String descriptionFolder, String descriptionFileName){
for (String fileNameExtension : markupLanguage.getFileNameExtensions()) {
java.nio.file.Path path = Paths.get(descriptionsFolderPath, descriptionFolder, descriptionFileName + fileNameExtension);
if (Files.isReadable(path)) {
if (logger.isInfoEnabled()) {
logger.info("Description file processed: {}", path);
}
return FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim();
try {
return FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim();
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn(String.format("Failed to read description file: %s", path), e);
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Description file is not readable: {}", path);
@@ -284,20 +311,26 @@ public class DefinitionsDocument extends MarkupDocument {
return null;
}
private void definitionSchema(String definitionName, MarkupDocBuilder docBuilder) throws IOException {
private void definitionSchema(String definitionName, MarkupDocBuilder docBuilder) {
if(schemasEnabled) {
if (StringUtils.isNotBlank(definitionName)) {
if (isNotBlank(definitionName)) {
schema(JSON_SCHEMA, schemasFolderPath, definitionName + JSON_SCHEMA_EXTENSION, JSON, docBuilder);
schema(XML_SCHEMA, schemasFolderPath, definitionName + XML_SCHEMA_EXTENSION, XML, docBuilder);
}
}
}
private void schema(String title, String schemasFolderPath, String schemaName, String language, MarkupDocBuilder docBuilder) throws IOException {
private void schema(String title, String schemasFolderPath, String schemaName, String language, MarkupDocBuilder docBuilder) {
java.nio.file.Path path = Paths.get(schemasFolderPath, schemaName);
if (Files.isReadable(path)) {
docBuilder.sectionTitleLevel3(title);
docBuilder.source(FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim(), language);
try {
docBuilder.source(FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim(), language);
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn(String.format("Failed to read schema file: %s", path), e);
}
}
if (logger.isInfoEnabled()) {
logger.info("Schema file processed: {}", path);
}

View File

@@ -18,6 +18,7 @@
*/
package io.github.robwin.swagger2markup.builder.document;
import io.github.robwin.swagger2markup.config.Swagger2MarkupConfig;
import io.swagger.models.Swagger;
import io.github.robwin.markup.builder.MarkupDocBuilder;
import io.github.robwin.markup.builder.MarkupDocBuilders;
@@ -48,9 +49,9 @@ public abstract class MarkupDocument {
protected MarkupLanguage markupLanguage;
protected MarkupDocBuilder markupDocBuilder;
MarkupDocument(Swagger swagger, MarkupLanguage markupLanguage){
this.swagger = swagger;
this.markupLanguage = markupLanguage;
MarkupDocument(Swagger2MarkupConfig swagger2MarkupConfig){
this.swagger = swagger2MarkupConfig.getSwagger();
this.markupLanguage = swagger2MarkupConfig.getMarkupLanguage();
this.markupDocBuilder = MarkupDocBuilders.documentBuilder(markupLanguage);
}

View File

@@ -18,15 +18,15 @@
*/
package io.github.robwin.swagger2markup.builder.document;
import io.github.robwin.swagger2markup.config.Swagger2MarkupConfig;
import io.swagger.models.*;
import io.github.robwin.markup.builder.MarkupLanguage;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.*;
public class OverviewDocument extends MarkupDocument {
private static final String OVERVIEW = "Overview";
@@ -44,18 +44,17 @@ public class OverviewDocument extends MarkupDocument {
private static final String BASE_PATH = "BasePath: ";
private static final String SCHEMES = "Schemes: ";
public OverviewDocument(Swagger swagger, MarkupLanguage markupLanguage){
super(swagger, markupLanguage);
public OverviewDocument(Swagger2MarkupConfig swagger2MarkupConfig){
super(swagger2MarkupConfig);
}
/**
* Builds the MarkupDocument.
*
* @return the built MarkupDocument
* @throws java.io.IOException if the files to include are not readable
*/
@Override
public MarkupDocument build() throws IOException {
public MarkupDocument build(){
overview();
return this;
}
@@ -68,11 +67,11 @@ public class OverviewDocument extends MarkupDocument {
Info info = swagger.getInfo();
this.markupDocBuilder.documentTitle(info.getTitle());
this.markupDocBuilder.sectionTitleLevel1(OVERVIEW);
if(StringUtils.isNotBlank(info.getDescription())){
if(isNotBlank(info.getDescription())){
this.markupDocBuilder.textLine(info.getDescription());
this.markupDocBuilder.newLine();
}
if(StringUtils.isNotBlank(info.getVersion())){
if(isNotBlank(info.getVersion())){
this.markupDocBuilder.sectionTitleLevel2(CURRENT_VERSION);
this.markupDocBuilder.textLine(VERSION + info.getVersion());
this.markupDocBuilder.newLine();
@@ -80,56 +79,56 @@ public class OverviewDocument extends MarkupDocument {
Contact contact = info.getContact();
if(contact != null){
this.markupDocBuilder.sectionTitleLevel2(CONTACT_INFORMATION);
if(StringUtils.isNotBlank(contact.getName())){
if(isNotBlank(contact.getName())){
this.markupDocBuilder.textLine(CONTACT_NAME + contact.getName());
}
if(StringUtils.isNotBlank(contact.getEmail())){
if(isNotBlank(contact.getEmail())){
this.markupDocBuilder.textLine(CONTACT_EMAIL + contact.getEmail());
}
this.markupDocBuilder.newLine();
}
License license = info.getLicense();
if(license != null && (StringUtils.isNotBlank(license.getName()) || StringUtils.isNotBlank(license.getUrl()))) {
if(license != null && (isNotBlank(license.getName()) || isNotBlank(license.getUrl()))) {
this.markupDocBuilder.sectionTitleLevel2(LICENSE_INFORMATION);
if (StringUtils.isNotBlank(license.getName())) {
if (isNotBlank(license.getName())) {
this.markupDocBuilder.textLine(LICENSE + license.getName());
}
if (StringUtils.isNotBlank(license.getUrl())) {
if (isNotBlank(license.getUrl())) {
this.markupDocBuilder.textLine(LICENSE_URL + license.getUrl());
}
this.markupDocBuilder.newLine();
}
if(StringUtils.isNotBlank(info.getTermsOfService())){
if(isNotBlank(info.getTermsOfService())){
this.markupDocBuilder.textLine(TERMS_OF_SERVICE + info.getTermsOfService());
this.markupDocBuilder.newLine();
}
if(StringUtils.isNotBlank(swagger.getHost()) || StringUtils.isNotBlank(swagger.getBasePath()) || CollectionUtils.isNotEmpty(swagger.getSchemes())) {
if(isNotBlank(swagger.getHost()) || isNotBlank(swagger.getBasePath()) || isNotEmpty(swagger.getSchemes())) {
this.markupDocBuilder.sectionTitleLevel2(URI_SCHEME);
if (StringUtils.isNotBlank(swagger.getHost())) {
if (isNotBlank(swagger.getHost())) {
this.markupDocBuilder.textLine(HOST + swagger.getHost());
}
if (StringUtils.isNotBlank(swagger.getBasePath())) {
if (isNotBlank(swagger.getBasePath())) {
this.markupDocBuilder.textLine(BASE_PATH + swagger.getBasePath());
}
if (CollectionUtils.isNotEmpty(swagger.getSchemes())) {
if (isNotEmpty(swagger.getSchemes())) {
List<String> schemes = new ArrayList<>();
for (Scheme scheme : swagger.getSchemes()) {
schemes.add(scheme.toString());
}
this.markupDocBuilder.textLine(SCHEMES + StringUtils.join(schemes, ", "));
this.markupDocBuilder.textLine(SCHEMES + join(schemes, ", "));
}
this.markupDocBuilder.newLine();
}
if(CollectionUtils.isNotEmpty(swagger.getTags())){
if(isNotEmpty(swagger.getTags())){
this.markupDocBuilder.sectionTitleLevel2(TAGS);
List<String> tags = new ArrayList<>();
for(Tag tag : swagger.getTags()){
String name = tag.getName();
String description = tag.getDescription();
if(StringUtils.isNoneBlank(description)){
if(isNoneBlank(description)){
tags.add(name + ": " + description);
}else{
tags.add(name);
@@ -139,13 +138,13 @@ public class OverviewDocument extends MarkupDocument {
this.markupDocBuilder.newLine();
}
if(CollectionUtils.isNotEmpty(swagger.getConsumes())){
if(isNotEmpty(swagger.getConsumes())){
this.markupDocBuilder.sectionTitleLevel2(CONSUMES);
this.markupDocBuilder.unorderedList(swagger.getConsumes());
this.markupDocBuilder.newLine();
}
if(CollectionUtils.isNotEmpty(swagger.getProduces())){
if(isNotEmpty(swagger.getProduces())){
this.markupDocBuilder.sectionTitleLevel2(PRODUCES);
this.markupDocBuilder.unorderedList(swagger.getProduces());
this.markupDocBuilder.newLine();

View File

@@ -18,29 +18,29 @@
*/
package io.github.robwin.swagger2markup.builder.document;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Response;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.properties.Property;
import io.github.robwin.markup.builder.MarkupLanguage;
import com.google.common.base.Optional;
import com.google.common.collect.Multimap;
import io.github.robwin.swagger2markup.GroupBy;
import io.github.robwin.swagger2markup.config.Swagger2MarkupConfig;
import io.github.robwin.swagger2markup.utils.ParameterUtils;
import io.github.robwin.swagger2markup.utils.PropertyUtils;
import io.swagger.models.*;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.properties.Property;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.commons.lang3.tuple.Pair;
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;
import java.util.*;
import static io.github.robwin.swagger2markup.utils.TagUtils.*;
import static org.apache.commons.lang3.StringUtils.*;
/**
* @author Robert Winkler
@@ -48,6 +48,7 @@ import java.util.Map;
public class PathsDocument extends MarkupDocument {
private static final String PATHS = "Paths";
private static final String RESOURCES = "Resources";
private static final String PARAMETERS = "Parameters";
private static final String RESPONSES = "Responses";
private static final String EXAMPLE_CURL = "Example CURL request";
@@ -65,16 +66,18 @@ public class PathsDocument extends MarkupDocument {
private String examplesFolderPath;
private boolean handWrittenDescriptionsEnabled;
private String descriptionsFolderPath;
private final GroupBy pathsGroupedBy;
public PathsDocument(Swagger swagger, MarkupLanguage markupLanguage, String examplesFolderPath, String descriptionsFolderPath){
super(swagger, markupLanguage);
if(StringUtils.isNotBlank(examplesFolderPath)){
public PathsDocument(Swagger2MarkupConfig swagger2MarkupConfig){
super(swagger2MarkupConfig);
this.pathsGroupedBy = swagger2MarkupConfig.getPathsGroupedBy();
if(isNotBlank(swagger2MarkupConfig.getExamplesFolderPath())){
this.examplesEnabled = true;
this.examplesFolderPath = examplesFolderPath;
this.examplesFolderPath = swagger2MarkupConfig.getExamplesFolderPath();
}
if(StringUtils.isNotBlank(descriptionsFolderPath)){
if(isNotBlank(swagger2MarkupConfig.getDescriptionsFolderPath())){
this.handWrittenDescriptionsEnabled = true;
this.descriptionsFolderPath = descriptionsFolderPath + "/" + PATHS.toLowerCase();
this.descriptionsFolderPath = swagger2MarkupConfig.getDescriptionsFolderPath() + "/" + PATHS.toLowerCase();
}
if(examplesEnabled){
if (logger.isDebugEnabled()) {
@@ -96,42 +99,70 @@ public class PathsDocument extends MarkupDocument {
}
}
/**
* Builds the paths markup document.
*
* @return the the paths markup document
* @throws IOException
*/
@Override
public MarkupDocument build() throws IOException {
public MarkupDocument build(){
paths();
return this;
}
/**
* Builds all paths of the Swagger model
* Builds all paths of the Swagger model. Either grouped as-is or by tags.
*/
private void paths() throws IOException {
private void paths(){
Map<String, Path> paths = swagger.getPaths();
if(MapUtils.isNotEmpty(paths)) {
this.markupDocBuilder.sectionTitleLevel1(PATHS);
for (Map.Entry<String, Path> entry : paths.entrySet()) {
Path path = entry.getValue();
if(path != null) {
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());
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel1(PATHS);
for (Map.Entry<String, Path> pathEntry : paths.entrySet()) {
Path path = pathEntry.getValue();
if(path != null) {
createPathSections(pathEntry.getKey(), path);
}
}
}else{
this.markupDocBuilder.sectionTitleLevel1(RESOURCES);
Multimap<String, Pair<String, Path>> pathsGroupedByTag = groupPathsByTag(paths);
Map<String, Tag> tagsMap = convertTagsListToMap(swagger.getTags());
for(String tagName : pathsGroupedByTag.keySet()){
this.markupDocBuilder.sectionTitleLevel2(WordUtils.capitalize(tagName));
Optional<String> tagDescription = getTagDescription(tagsMap, tagName);
if(tagDescription.isPresent()) {
this.markupDocBuilder.paragraph(tagDescription.get());
}
Collection<Pair<String, Path>> pathsOfTag = pathsGroupedByTag.get(tagName);
for(Pair<String, Path> pathPair : pathsOfTag){
Path path = pathPair.getValue();
if(path != null) {
createPathSections(pathPair.getKey(), path);
}
}
}
}
}
}
private void createPathSections(String pathUrl, Path path){
for(Map.Entry<HttpMethod, Operation> operationEntry : path.getOperationMap().entrySet()){
String methodAndPath = operationEntry.getKey() + " " + pathUrl;
path(methodAndPath, operationEntry.getValue());
}
}
/**
* Builds a path
* Builds a path.
*
* @param httpMethod the HTTP method of the path
* @param resourcePath the URL of the path
* @param methodAndPath the Method of the operation and the URL of the path
* @param operation the Swagger Operation
*/
private void path(String httpMethod, String resourcePath, Operation operation) throws IOException {
private void path(String methodAndPath, Operation operation) {
if(operation != null){
pathTitle(httpMethod, resourcePath, operation);
pathTitle(methodAndPath, operation);
descriptionSection(operation);
parametersSection(operation);
responsesSection(operation);
@@ -142,63 +173,89 @@ public class PathsDocument extends MarkupDocument {
}
}
private void pathTitle(String httpMethod, String resourcePath, Operation operation) {
/**
* Adds the path title to the document. If the operation has a summary, the title is the summary.
* Otherwise the title is the method of the operation and the URL of the path.
*
* @param methodAndPath the Method of the operation and the URL of the path
* @param operation the Swagger Operation
*/
private void pathTitle(String methodAndPath, Operation operation) {
String summary = operation.getSummary();
String title;
if(StringUtils.isNotBlank(summary)) {
if(isNotBlank(summary)) {
title = summary;
this.markupDocBuilder.sectionTitleLevel2(title);
this.markupDocBuilder.listing(httpMethod + " " + resourcePath);
addPathTitle(title);
this.markupDocBuilder.listing(methodAndPath);
}else{
title = httpMethod + " " + resourcePath;
this.markupDocBuilder.sectionTitleLevel2(title);
addPathTitle(methodAndPath);
}
if (logger.isInfoEnabled()) {
logger.info("Path processed: {}", title);
logger.info("Path processed: {}", methodAndPath);
}
}
private void descriptionSection(Operation operation) throws IOException {
/**
* Adds a path title to the document.
*
* @param title the path title
*/
private void addPathTitle(String title) {
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel2(title);
}else{
this.markupDocBuilder.sectionTitleLevel3(title);
}
}
/**
* Adds a path description to the document.
*
* @param operation the Swagger Operation
*/
private void descriptionSection(Operation operation) {
if(handWrittenDescriptionsEnabled){
String summary = operation.getSummary();
if(StringUtils.isNotBlank(summary)) {
if(isNotBlank(summary)) {
String operationFolder = summary.replace(".", "").replace(" ", "_").toLowerCase();
String description = handWrittenPathDescription(operationFolder, DESCRIPTION_FILE_NAME);
if(StringUtils.isNotBlank(description)){
this.markupDocBuilder.sectionTitleLevel3(DESCRIPTION);
this.markupDocBuilder.paragraph(description);
Optional<String> description = handWrittenPathDescription(operationFolder, DESCRIPTION_FILE_NAME);
if(description.isPresent()){
pathDescription(description.get());
}else{
if (logger.isInfoEnabled()) {
logger.info("Hand-written description cannot be read. Trying to use description from Swagger source.");
}
pathDescription(operation);
pathDescription(operation.getDescription());
}
}else{
if (logger.isInfoEnabled()) {
logger.info("Hand-written description cannot be read, because summary of operation is empty. Trying to use description from Swagger source.");
}
pathDescription(operation);
pathDescription(operation.getDescription());
}
}else {
pathDescription(operation);
pathDescription(operation.getDescription());
}
}
private void pathDescription(Operation operation) {
String description = operation.getDescription();
if (StringUtils.isNotBlank(description)) {
this.markupDocBuilder.sectionTitleLevel3(DESCRIPTION);
private void pathDescription(String description) {
if (isNotBlank(description)) {
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(DESCRIPTION);
}else{
this.markupDocBuilder.sectionTitleLevel4(DESCRIPTION);
}
this.markupDocBuilder.paragraph(description);
}
}
private void parametersSection(Operation operation) throws IOException {
private void parametersSection(Operation operation) {
List<Parameter> parameters = operation.getParameters();
if(CollectionUtils.isNotEmpty(parameters)){
List<String> headerAndContent = new ArrayList<>();
// Table header row
List<String> header = Arrays.asList(TYPE_COLUMN, NAME_COLUMN, DESCRIPTION_COLUMN, REQUIRED_COLUMN, SCHEMA_COLUMN, DEFAULT_COLUMN);
headerAndContent.add(StringUtils.join(header, DELIMITER));
headerAndContent.add(join(header, DELIMITER));
for(Parameter parameter : parameters){
String type = ParameterUtils.getType(parameter, markupLanguage);
String parameterType = WordUtils.capitalize(parameter.getIn() + PARAMETER);
@@ -209,43 +266,61 @@ public class PathsDocument extends MarkupDocument {
parameterDescription(operation, parameter),
Boolean.toString(parameter.getRequired()), type,
ParameterUtils.getDefaultValue(parameter));
headerAndContent.add(StringUtils.join(content, DELIMITER));
headerAndContent.add(join(content, DELIMITER));
}
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(PARAMETERS);
}else{
this.markupDocBuilder.sectionTitleLevel4(PARAMETERS);
}
this.markupDocBuilder.sectionTitleLevel3(PARAMETERS);
this.markupDocBuilder.tableWithHeaderRow(headerAndContent);
}
}
private String parameterDescription(Operation operation, Parameter parameter) throws IOException {
String description;
/**
* Retrieves the description of a parameter, or otherwise an empty String.
* If hand-written descriptions are enabled, it tries to load the description from a file.
* If the file cannot be read, the description the parameter is returned.
*
* @param operation the Swagger Operation
* @param parameter the Swagger Parameter
* @return the description of a parameter.
*/
private String parameterDescription(Operation operation, Parameter parameter){
if(handWrittenDescriptionsEnabled){
String summary = operation.getSummary();
String operationFolder = summary.replace(".", "").replace(" ", "_").toLowerCase();
String parameterName = parameter.getName();
if(StringUtils.isNotBlank(operationFolder) && StringUtils.isNotBlank(parameterName)) {
description = handWrittenPathDescription(operationFolder + "/" + parameterName, DESCRIPTION_FILE_NAME);
if(StringUtils.isBlank(description)) {
if (logger.isInfoEnabled()) {
logger.info("Hand-written description file cannot be read. Trying to use description from Swagger source.");
if(isNotBlank(operationFolder) && isNotBlank(parameterName)) {
Optional<String> description = handWrittenPathDescription(operationFolder + "/" + parameterName, DESCRIPTION_FILE_NAME);
if(description.isPresent()){
return description.get();
}
else{
if (logger.isWarnEnabled()) {
logger.warn("Hand-written description file cannot be read. Trying to use description from Swagger source.");
}
description = StringUtils.defaultString(parameter.getDescription());
return defaultString(parameter.getDescription());
}
}else{
if (logger.isInfoEnabled()) {
logger.info("Hand-written description file cannot be read, because summary of operation or name of parameter is empty. Trying to use description from Swagger source.");
if (logger.isWarnEnabled()) {
logger.warn("Hand-written description file cannot be read, because summary of operation or name of parameter is empty. Trying to use description from Swagger source.");
}
description = StringUtils.defaultString(parameter.getDescription());
return defaultString(parameter.getDescription());
}
}else {
description = StringUtils.defaultString(parameter.getDescription());
return defaultString(parameter.getDescription());
}
return description;
}
private void consumesSection(Operation operation) {
List<String> consumes = operation.getConsumes();
if(CollectionUtils.isNotEmpty(consumes)){
this.markupDocBuilder.sectionTitleLevel3(CONSUMES);
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(CONSUMES);
}else{
this.markupDocBuilder.sectionTitleLevel4(CONSUMES);
}
this.markupDocBuilder.unorderedList(consumes);
}
@@ -254,45 +329,64 @@ public class PathsDocument extends MarkupDocument {
private void producesSection(Operation operation) {
List<String> produces = operation.getProduces();
if(CollectionUtils.isNotEmpty(produces)){
this.markupDocBuilder.sectionTitleLevel3(PRODUCES);
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(PRODUCES);
}else{
this.markupDocBuilder.sectionTitleLevel4(PRODUCES);
}
this.markupDocBuilder.unorderedList(produces);
}
}
private void tagsSection(Operation operation) {
List<String> tags = operation.getTags();
if(CollectionUtils.isNotEmpty(tags)){
this.markupDocBuilder.sectionTitleLevel3(TAGS);
this.markupDocBuilder.unorderedList(tags);
if(pathsGroupedBy.equals(GroupBy.AS_IS)) {
List<String> tags = operation.getTags();
if (CollectionUtils.isNotEmpty(tags)) {
this.markupDocBuilder.sectionTitleLevel3(TAGS);
this.markupDocBuilder.unorderedList(tags);
}
}
}
/**
* Builds the example section of a Swagger Operation
* Builds the example section of a Swagger Operation. Tries to load the examples from
* curl-request.adoc, http-request.adoc and http-response.adoc or
* curl-request.md, http-request.md and http-response.md.
*
* @param operation the Swagger Operation
* @throws IOException if the example file is not readable
*/
private void examplesSection(Operation operation) throws IOException {
private void examplesSection(Operation operation) {
if(examplesEnabled){
String summary = operation.getSummary();
if(StringUtils.isNotBlank(summary)) {
if(isNotBlank(summary)) {
String exampleFolder = summary.replace(".", "").replace(" ", "_").toLowerCase();
String curlExample = example(exampleFolder, CURL_EXAMPLE_FILE_NAME);
if(StringUtils.isNotBlank(curlExample)){
this.markupDocBuilder.sectionTitleLevel3(EXAMPLE_CURL);
this.markupDocBuilder.paragraph(curlExample);
Optional<String> curlExample = example(exampleFolder, CURL_EXAMPLE_FILE_NAME);
if(curlExample.isPresent()){
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(EXAMPLE_CURL);
}else{
this.markupDocBuilder.sectionTitleLevel4(EXAMPLE_CURL);
}
this.markupDocBuilder.paragraph(curlExample.get());
}
String requestExample = example(exampleFolder, REQUEST_EXAMPLE_FILE_NAME);
if(StringUtils.isNotBlank(requestExample)){
this.markupDocBuilder.sectionTitleLevel3(EXAMPLE_REQUEST);
this.markupDocBuilder.paragraph(requestExample);
Optional<String> requestExample = example(exampleFolder, REQUEST_EXAMPLE_FILE_NAME);
if(requestExample.isPresent()){
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(EXAMPLE_REQUEST);
}else{
this.markupDocBuilder.sectionTitleLevel4(EXAMPLE_REQUEST);
}
this.markupDocBuilder.paragraph(requestExample.get());
}
String responseExample = example(exampleFolder, RESPONSE_EXAMPLE_FILE_NAME);
if(StringUtils.isNotBlank(responseExample)){
this.markupDocBuilder.sectionTitleLevel3(EXAMPLE_RESPONSE);
this.markupDocBuilder.paragraph(responseExample);
Optional<String> responseExample = example(exampleFolder, RESPONSE_EXAMPLE_FILE_NAME);
if(responseExample.isPresent()){
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(EXAMPLE_RESPONSE);
}else{
this.markupDocBuilder.sectionTitleLevel4(EXAMPLE_RESPONSE);
}
this.markupDocBuilder.paragraph(responseExample.get());
}
}else{
if (logger.isWarnEnabled()) {
@@ -308,26 +402,31 @@ public class PathsDocument extends MarkupDocument {
* @param exampleFolder the name of the folder where the example file resides
* @param exampleFileName the name of the example file
* @return the content of the file
* @throws IOException
*/
private String example(String exampleFolder, String exampleFileName) throws IOException {
private Optional<String> example(String exampleFolder, String exampleFileName) {
for (String fileNameExtension : markupLanguage.getFileNameExtensions()) {
java.nio.file.Path path = Paths.get(examplesFolderPath, exampleFolder, exampleFileName + fileNameExtension);
if (Files.isReadable(path)) {
if (logger.isInfoEnabled()) {
logger.info("Example file processed: {}", path);
}
return FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim();
try {
return Optional.fromNullable(FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim());
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn(String.format("Failed to read example file: %s", path), e);
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Example file is not readable: {}", path);
if (logger.isWarnEnabled()) {
logger.warn("Example file is not readable: {}", path);
}
}
}
if (logger.isWarnEnabled()) {
logger.info("No example file found with correct file name extension in folder: {}", Paths.get(examplesFolderPath, exampleFolder));
logger.warn("No example file found with correct file name extension in folder: {}", Paths.get(examplesFolderPath, exampleFolder));
}
return null;
return Optional.absent();
}
/**
@@ -336,26 +435,31 @@ public class PathsDocument extends MarkupDocument {
* @param descriptionFolder the name of the folder where the description file resides
* @param descriptionFileName the name of the description file
* @return the content of the file
* @throws IOException
*/
private String handWrittenPathDescription(String descriptionFolder, String descriptionFileName) throws IOException {
private Optional<String> handWrittenPathDescription(String descriptionFolder, String descriptionFileName){
for (String fileNameExtension : markupLanguage.getFileNameExtensions()) {
java.nio.file.Path path = Paths.get(descriptionsFolderPath, descriptionFolder, descriptionFileName + fileNameExtension);
if (Files.isReadable(path)) {
if (logger.isInfoEnabled()) {
logger.info("Description file processed: {}", path);
}
return FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim();
try {
return Optional.fromNullable(FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8).trim());
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn(String.format("Failed to read description file: %s", path), e);
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Description file is not readable: {}", path);
if (logger.isWarnEnabled()) {
logger.warn("Description file is not readable: {}", path);
}
}
}
if (logger.isWarnEnabled()) {
logger.info("No description file found with correct file name extension in folder: {}", Paths.get(descriptionsFolderPath, descriptionFolder));
logger.warn("No description file found with correct file name extension in folder: {}", Paths.get(descriptionsFolderPath, descriptionFolder));
}
return null;
return Optional.absent();
}
private void responsesSection(Operation operation) {
@@ -373,7 +477,11 @@ public class PathsDocument extends MarkupDocument {
csvContent.add(entry.getKey() + DELIMITER + response.getDescription() + DELIMITER + "No Content");
}
}
this.markupDocBuilder.sectionTitleLevel3(RESPONSES);
if(pathsGroupedBy.equals(GroupBy.AS_IS)){
this.markupDocBuilder.sectionTitleLevel3(RESPONSES);
}else{
this.markupDocBuilder.sectionTitleLevel4(RESPONSES);
}
this.markupDocBuilder.tableWithHeaderRow(csvContent);
}
}

View File

@@ -0,0 +1,90 @@
/*
*
* 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.
*
*
*/
package io.github.robwin.swagger2markup.config;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.github.robwin.swagger2markup.GroupBy;
import io.github.robwin.swagger2markup.OrderBy;
import io.swagger.models.Swagger;
public class Swagger2MarkupConfig {
private final Swagger swagger;
private final MarkupLanguage markupLanguage;
private final String examplesFolderPath;
private final String schemasFolderPath;
private final String descriptionsFolderPath;
private final boolean separatedDefinitions;
private final GroupBy pathsGroupedBy;
private final OrderBy definitionsOrderedBy;
/**
* @param swagger the Swagger source
* @param markupLanguage the markup language which is used to generate the files
* @param examplesFolderPath examplesFolderPath the path to the folder where the example documents reside
* @param schemasFolderPath the path to the folder where the schema documents reside
* @param descriptionsFolderPath the path to the folder where the description documents reside
* @param separatedDefinitions specified if in addition to the definitions file, also separate definition files for each model definition should be created
* @param pathsGroupedBy specifies if the paths should be grouped by tags or stay as-is
* @param definitionsOrderedBy specifies if the definitions should be ordered by natural ordering or stay as-is
*/
public Swagger2MarkupConfig(Swagger swagger, MarkupLanguage markupLanguage, String examplesFolderPath, String schemasFolderPath, String descriptionsFolderPath, boolean separatedDefinitions, GroupBy pathsGroupedBy, OrderBy definitionsOrderedBy) {
this.swagger = swagger;
this.markupLanguage = markupLanguage;
this.examplesFolderPath = examplesFolderPath;
this.schemasFolderPath = schemasFolderPath;
this.descriptionsFolderPath = descriptionsFolderPath;
this.separatedDefinitions = separatedDefinitions;
this.pathsGroupedBy = pathsGroupedBy;
this.definitionsOrderedBy = definitionsOrderedBy;
}
public Swagger getSwagger() {
return swagger;
}
public MarkupLanguage getMarkupLanguage() {
return markupLanguage;
}
public String getExamplesFolderPath() {
return examplesFolderPath;
}
public String getSchemasFolderPath() {
return schemasFolderPath;
}
public String getDescriptionsFolderPath() {
return descriptionsFolderPath;
}
public boolean isSeparatedDefinitions() {
return separatedDefinitions;
}
public GroupBy getPathsGroupedBy() {
return pathsGroupedBy;
}
public OrderBy getDefinitionsOrderedBy() {
return definitionsOrderedBy;
}
}

View File

@@ -27,6 +27,13 @@ import org.apache.commons.lang3.Validate;
public final class ModelUtils {
/**
* Retrieves the type of a model, or otherwise "NOT FOUND"
*
* @param model the model
* @param markupLanguage the markup language which is used to generate the files
* @return the type of the model, or otherwise "NOT FOUND"
*/
public static String getType(Model model, MarkupLanguage markupLanguage) {
Validate.notNull(model, "model must not be null!");
if (model instanceof ModelImpl) {

View File

@@ -18,21 +18,29 @@
*/
package io.github.robwin.swagger2markup.utils;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.swagger.models.Model;
import io.swagger.models.parameters.AbstractSerializableParameter;
import io.swagger.models.parameters.BodyParameter;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.parameters.RefParameter;
import io.github.robwin.markup.builder.MarkupLanguage;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.*;
public final class ParameterUtils {
/**
* Retrieves the type of a parameter, or otherwise an empty String
*
* @param parameter the parameter
* @param markupLanguage the markup language which is used to generate the files
* @return the type of the parameter, or otherwise an empty String
*/
public static String getType(Parameter parameter, MarkupLanguage markupLanguage){
Validate.notNull(parameter, "property must not be null!");
String type = "NOT FOUND";
@@ -50,7 +58,7 @@ public final class ParameterUtils {
AbstractSerializableParameter serializableParameter = (AbstractSerializableParameter)parameter;
List enums = serializableParameter.getEnum();
if(CollectionUtils.isNotEmpty(enums)){
type = "enum" + " (" + StringUtils.join(enums, ", ") + ")";
type = "enum" + " (" + join(enums, ", ") + ")";
}else{
type = getTypeWithFormat(serializableParameter.getType(), serializableParameter.getFormat());
}
@@ -66,19 +74,32 @@ public final class ParameterUtils {
default: return refParameter.getSimpleRef();
}
}
return StringUtils.defaultString(type);
return defaultString(type);
}
/**
* Adds the format to the type, if a format is available
*
* @param typeWithoutFormat the type
* @param format the format
* @return returns the type and format, if a format is available
*/
private static String getTypeWithFormat(String typeWithoutFormat, String format) {
String type;
if(StringUtils.isNotBlank(format)){
type = StringUtils.defaultString(typeWithoutFormat) + " (" + format + ")";
if(isNotBlank(format)){
type = defaultString(typeWithoutFormat) + " (" + format + ")";
}else{
type = StringUtils.defaultString(typeWithoutFormat);
type = defaultString(typeWithoutFormat);
}
return type;
}
/**
* Retrieves the default value of a parameter, or otherwise an empty String
*
* @param parameter the parameter
* @return the default value of the parameter, or otherwise an empty String
*/
public static String getDefaultValue(Parameter parameter){
Validate.notNull(parameter, "property must not be null!");
String defaultValue = "";
@@ -86,7 +107,7 @@ public final class ParameterUtils {
AbstractSerializableParameter serializableParameter = (AbstractSerializableParameter)parameter;
defaultValue = serializableParameter.getDefaultValue();
}
return StringUtils.defaultString(defaultValue);
return defaultString(defaultValue);
}
}

View File

@@ -18,17 +18,25 @@
*/
package io.github.robwin.swagger2markup.utils;
import io.swagger.models.properties.*;
import io.github.robwin.markup.builder.MarkupLanguage;
import io.swagger.models.properties.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import java.util.List;
import java.util.Objects;
import static org.apache.commons.lang3.StringUtils.*;
public final class PropertyUtils {
/**
* Retrieves the type and format of a property.
*
* @param property the property
* @param markupLanguage the markup language which is used to generate the files
* @return the type of the property
*/
public static String getType(Property property, MarkupLanguage markupLanguage){
Validate.notNull(property, "property must not be null!");
String type;
@@ -46,21 +54,27 @@ public final class PropertyUtils {
StringProperty stringProperty = (StringProperty)property;
List<String> enums = stringProperty.getEnum();
if(CollectionUtils.isNotEmpty(enums)){
type = "enum" + " (" + StringUtils.join(enums, ", ") + ")";
type = "enum" + " (" + join(enums, ", ") + ")";
}else{
type = property.getType();
}
}
else{
if(StringUtils.isNotBlank(property.getFormat())){
type = StringUtils.defaultString(property.getType()) + " (" + property.getFormat() + ")";
if(isNotBlank(property.getFormat())){
type = defaultString(property.getType()) + " (" + property.getFormat() + ")";
}else{
type = property.getType();
}
}
return StringUtils.defaultString(type);
return defaultString(type);
}
/**
* Retrieves the default value of a property, or otherwise returns an empty String.
*
* @param property the property
* @return the default value of the property, or otherwise an empty String
*/
public static String getDefaultValue(Property property){
Validate.notNull(property, "property must not be null!");
String defaultValue = "";

View File

@@ -0,0 +1,102 @@
/*
*
* 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.
*
*
*/
package io.github.robwin.swagger2markup.utils;
import com.google.common.base.Optional;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import io.swagger.models.HttpMethod;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Tag;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TagUtils {
private static Logger LOG = LoggerFactory.getLogger(TagUtils.class);
/**
* Converts the global Tag list into a Map where the tag name is the key and the Tag the value.
*
* @param tags the List of tags
* @return the Map of tags
*/
public static Map<String, Tag> convertTagsListToMap(List<Tag> tags) {
if (tags == null) {
tags = new ArrayList<>();
}
Map<String, Tag> tagsMap = new HashMap<>();
for (Tag tag : tags) tagsMap.put(tag.getName(), tag);
return tagsMap;
}
/**
* Retrieves the optional description of a tag.
*
* @param tagsMap the Map of tags
* @param tagName the name of the tag
* @return the optional description of the tag
*/
public static Optional<String> getTagDescription(Map<String, Tag> tagsMap, String tagName) {
Tag tag = tagsMap.get(tagName);
if(tag != null){
return Optional.fromNullable(tag.getDescription());
}
return Optional.absent();
}
/**
* Groups the paths by tag. The key of the Multimap is the tag name.
* The value of the Multimap is a Pair which contains the Method and the Path.
*
* @param paths the Paths
* @return Paths grouped by Tag
*/
public static Multimap<String, Pair<String, Path>> groupPathsByTag(Map<String, Path> paths) {
Multimap<String, Pair<String, Path>> pathsGroupedByTag = MultimapBuilder.SortedSetMultimapBuilder.treeKeys().hashSetValues().build();
for (Map.Entry<String, Path> pathEntry : paths.entrySet()) {
String resourcePath = pathEntry.getKey();
Path path = pathEntry.getValue();
for(Map.Entry<HttpMethod, Operation> operationEntry : path.getOperationMap().entrySet()){
HttpMethod httpMethod = operationEntry.getKey();
Operation operation = operationEntry.getValue();
if(operation != null) {
List<String> tags = operation.getTags();
Validate.notEmpty(tags, "Path operations must have tags, if you want to group by tags! The operation '%s %s' has not tags.", httpMethod, resourcePath);
for (String tag : tags) {
if (LOG.isInfoEnabled()) {
LOG.info("Added path operation '{} {}' to tag '{}'", httpMethod, resourcePath, tag);
}
pathsGroupedByTag.put(tag, Pair.of(resourcePath, pathEntry.getValue()));
}
}
}
}
return pathsGroupedByTag;
}
}

View File

@@ -37,6 +37,7 @@ import java.util.Set;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.assertj.core.api.BDDAssertions.assertThat;
public class Swagger2MarkupConverterTest {
@@ -57,11 +58,46 @@ public class Swagger2MarkupConverterTest {
assertThat(directories).hasSize(3).containsAll(asList("definitions.adoc", "overview.adoc", "paths.adoc"));
}
@Test
public void testSwagger2AsciiDocGroupedByTags() throws IOException {
//Given
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger.json").getFile());
File outputDirectory = new File("build/docs/asciidoc/generated");
FileUtils.deleteQuietly(outputDirectory);
//When
Swagger2MarkupConverter.from(file.getAbsolutePath())
.withPathsGroupedBy(GroupBy.TAGS)
.build()
.intoFolder(outputDirectory.getAbsolutePath());
//Then
String[] directories = outputDirectory.list();
assertThat(directories).hasSize(3).containsAll(asList("definitions.adoc", "overview.adoc", "paths.adoc"));
}
@Test
public void testSwagger2AsciiDocGroupedByTagsWithMissingTag() throws IOException {
//Given
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger_missing_tag.json").getFile());
File outputDirectory = new File("build/docs/asciidoc/generated");
FileUtils.deleteQuietly(outputDirectory);
//When
try {
Swagger2MarkupConverter.from(file.getAbsolutePath())
.withPathsGroupedBy(GroupBy.TAGS)
.build()
.intoFolder(outputDirectory.getAbsolutePath());
// If NullPointerException was not thrown, test would fail the specified message
failBecauseExceptionWasNotThrown(NullPointerException.class);
} catch (Exception e) {
assertThat(e).hasMessage("Path operations must have tags, if you want to group by tags! The operation 'PUT /pets' has not tags.");
}
}
@Test
public void testOldSwaggerSpec2AsciiDocConversion() throws IOException {
//Given
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger_12.json").getFile());
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/error_swagger_12.json").getFile());
File outputDirectory = new File("build/docs/asciidoc/generated");
FileUtils.deleteQuietly(outputDirectory);
@@ -187,7 +223,7 @@ public class Swagger2MarkupConverterTest {
public void testSwagger2MarkdownConversionWithSeparatedDefinitions() throws IOException {
//Given
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger.json").getFile());
File outputDirectory = new File("build/docs/asciidoc/generated");
File outputDirectory = new File("build/docs/markdown/generated");
FileUtils.deleteQuietly(outputDirectory);
//When
@@ -208,7 +244,7 @@ public class Swagger2MarkupConverterTest {
public void testSwagger2MarkdownConversionHandlesComposition() throws IOException {
//Given
File file = new File(Swagger2MarkupConverterTest.class.getResource("/json/swagger.json").getFile());
File outputDirectory = new File("build/docs/asciidoc/generated");
File outputDirectory = new File("build/docs/markdown/generated");
FileUtils.deleteQuietly(outputDirectory);
//When

View File

@@ -0,0 +1,108 @@
{
"apiVersion": "1",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8082/query-rs",
"resourcePath": "/query-services",
"apis": [
{
"path": "/query-services/timesheet",
"operations": [
{
"method": "GET",
"summary": "testTimesheet",
"responseClass": "void",
"nickname": "testTimeSheet",
"position": 0,
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"required": false,
"allowMultiple": false,
"dataType": "TimesheetEntryWireBase",
"paramType": "body"
}
]
}
]
}
],
"models": {
"TimesheetEntryWireBase": {
"id": "TimesheetEntryWireBase",
"name": "TimesheetEntryWireBase",
"qualifiedType": "com.yt.nss.rest.domain.test.TimesheetEntryWireBase",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"qualifiedType": "java.lang.String",
"position": 0,
"allowableValues": {
"values": [
"STRING"
],
"valueType": "LIST"
}
}
},
"description": "Basic class for timesheet entry",
"baseModel": "java.lang.Void",
"discriminator": "type",
"subTypes": [
"com.yt.nss.rest.domain.test.TimesheetProjectEntryWire",
"com.yt.nss.rest.domain.test.TimesheetAdminEntryWire"
]
},
"TimesheetProjectEntryWire": {
"id": "TimesheetProjectEntryWire",
"name": "TimesheetProjectEntryWire",
"qualifiedType": "com.yt.nss.rest.domain.test.TimesheetProjectEntryWire",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"qualifiedType": "java.lang.String",
"position": 0,
"allowableValues": {
"values": [
"STRING"
],
"valueType": "LIST"
}
}
}
},
"TimesheetAdminEntryWire": {
"id": "TimesheetAdminEntryWire",
"name": "TimesheetAdminEntryWire",
"qualifiedType": "com.yt.nss.rest.domain.test.TimesheetAdminEntryWire",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"qualifiedType": "java.lang.String",
"position": 0,
"allowableValues": {
"values": [
"STRING"
],
"valueType": "LIST"
}
}
}
}
},
"position": 1
}

View File

@@ -18,6 +18,20 @@
"schemes": [
"http"
],
"tags": [
{
"name": "pet",
"description": "Pet resource"
},
{
"name": "store",
"description": "Store resource"
},
{
"name": "user",
"description": "User resource"
}
],
"paths": {
"/pets": {
"post": {

View File

@@ -0,0 +1,883 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server.\n\n[Learn about Swagger](http://swagger.wordnik.com) or join the IRC channel `#swagger` on irc.freenode.net.\n\nFor this sample, you can use the api key `special-key` to test the authorization filters\n",
"version": "1.0.0",
"title": "Swagger Petstore API",
"termsOfService": "http://helloreverb.com/terms/",
"contact": {
"name": "apiteam@wordnik.com"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"host": "petstore.swagger.wordnik.com",
"basePath": "/v2",
"schemes": [
"http"
],
"tags": [
{
"name": "pet",
"description": "Pet resource"
},
{
"name": "store",
"description": "Store resource"
},
{
"name": "user",
"description": "User resource"
}
],
"paths": {
"/pets": {
"post": {
"tags": [
"pet"
],
"summary": "Add a new pet to the store",
"description": "",
"operationId": "addPet",
"consumes": [
"application/json",
"application/xml"
],
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "Pet object that needs to be added to the store",
"required": false,
"schema": {
"$ref": "#/definitions/Pet"
}
}
],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
},
"put": {
"summary": "Update an existing pet",
"description": "",
"operationId": "updatePet",
"consumes": [
"application/json",
"application/xml"
],
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "Pet object that needs to be added to the store",
"required": false,
"schema": {
"$ref": "#/definitions/Pet"
}
}
],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
},
"405": {
"description": "Validation exception"
}
},
"security": [
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
}
},
"/pets/findByStatus": {
"get": {
"tags": [
"pet"
],
"summary": "Finds Pets by status",
"description": "Multiple status values can be provided with comma seperated strings",
"operationId": "findPetsByStatus",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "query",
"name": "status",
"description": "Status values that need to be considered for filter",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
}
],
"responses": {
"200": {
"$ref": "#/responses/FoundPets"
},
"400": {
"description": "Invalid status value"
}
},
"security": [
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
}
},
"/pets/findByTags": {
"get": {
"tags": [
"pet"
],
"summary": "Finds Pets by tags",
"description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.",
"operationId": "findPetsByTags",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "query",
"name": "tags",
"description": "Tags to filter by",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
}
],
"responses": {
"200": {
"$ref": "#/responses/FoundPets"
},
"400": {
"description": "Invalid tag value"
}
},
"security": [
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
}
},
"/pets/{petId}": {
"get": {
"tags": [
"pet"
],
"summary": "Find pet by ID",
"description": "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions",
"operationId": "getPetById",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"$ref": "#/parameters/petId"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Pet"
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
}
},
"security": [
{
"api_key": []
},
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
},
"post": {
"tags": [
"pet"
],
"summary": "Updates a pet in the store with form data",
"description": "",
"operationId": "updatePetWithForm",
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "path",
"name": "petId",
"description": "ID of pet that needs to be updated",
"required": true,
"type": "string"
},
{
"in": "formData",
"name": "name",
"description": "Updated name of the pet",
"required": true,
"type": "string"
},
{
"in": "formData",
"name": "status",
"description": "Updated status of the pet",
"required": true,
"type": "string"
}
],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
},
"delete": {
"tags": [
"pet"
],
"summary": "Deletes a pet",
"description": "",
"operationId": "deletePet",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "header",
"name": "api_key",
"description": "",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "petId",
"description": "Pet id to delete",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"400": {
"description": "Invalid pet value"
}
},
"security": [
{
"petstore_auth": [
"write_pets",
"read_pets"
]
}
]
}
},
"/stores/order": {
"post": {
"tags": [
"store"
],
"summary": "Place an order for a pet",
"description": "",
"operationId": "placeOrder",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "order placed for purchasing the pet",
"required": false,
"schema": {
"$ref": "#/definitions/Order"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Order"
}
},
"400": {
"description": "Invalid Order"
}
}
}
},
"/stores/order/{orderId}": {
"get": {
"tags": [
"store"
],
"summary": "Find purchase order by ID",
"description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",
"operationId": "getOrderById",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "path",
"name": "orderId",
"description": "ID of pet that needs to be fetched",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Order"
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
},
"delete": {
"tags": [
"store"
],
"summary": "Delete purchase order by ID",
"description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
"operationId": "deleteOrder",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "path",
"name": "orderId",
"description": "ID of the order that needs to be deleted",
"required": true,
"type": "string"
}
],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
}
},
"/users": {
"post": {
"tags": [
"user"
],
"summary": "Create user",
"description": "This can only be done by the logged in user.",
"operationId": "createUser",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "Created user object",
"required": false,
"schema": {
"$ref": "#/definitions/User"
}
}
],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/users/createWithArray": {
"post": {
"tags": [
"user"
],
"summary": "Creates list of users with given input array",
"description": "",
"operationId": "createUsersWithArrayInput",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "List of user object",
"required": false,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
}
],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/users/createWithList": {
"post": {
"tags": [
"user"
],
"summary": "Creates list of users with given input array",
"description": "",
"operationId": "createUsersWithListInput",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "List of user object",
"required": false,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
}
],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/users/login": {
"get": {
"tags": [
"user"
],
"summary": "Logs user into the system",
"description": "",
"operationId": "loginUser",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "query",
"name": "username",
"description": "The user name for login",
"required": false,
"type": "string",
"default": "testUser"
},
{
"in": "query",
"name": "password",
"description": "The password for login in clear text",
"required": false,
"type": "string",
"default": "testPassword"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "string"
}
},
"400": {
"description": "Invalid username/password supplied"
}
}
}
},
"/users/logout": {
"get": {
"tags": [
"user"
],
"summary": "Logs out current logged in user session",
"description": "",
"operationId": "logoutUser",
"produces": [
"application/json",
"application/xml"
],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/users/{username}": {
"get": {
"tags": [
"user"
],
"summary": "Get user by user name",
"description": "",
"operationId": "getUserByName",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "path",
"name": "username",
"description": "The name that needs to be fetched. Use user1 for testing.",
"required": true,
"type": "string",
"default": "testUser"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/User"
}
},
"400": {
"description": "Invalid username supplied"
},
"404": {
"description": "User not found"
}
}
},
"put": {
"tags": [
"user"
],
"summary": "Updated user",
"description": "This can only be done by the logged in user.",
"operationId": "updateUser",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "path",
"name": "username",
"description": "name that need to be deleted",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "Updated user object",
"required": false,
"schema": {
"$ref": "#/definitions/User"
}
}
],
"responses": {
"400": {
"description": "Invalid user supplied"
},
"404": {
"description": "User not found"
}
}
},
"delete": {
"tags": [
"user"
],
"summary": "Delete user",
"description": "This can only be done by the logged in user.",
"operationId": "deleteUser",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "path",
"name": "username",
"description": "The name that needs to be deleted",
"required": true,
"type": "string"
}
],
"responses": {
"400": {
"description": "Invalid username supplied"
},
"404": {
"description": "User not found"
}
}
}
}
},
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
},
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://petstore.swagger.wordnik.com/api/oauth/dialog",
"flow": "implicit",
"scopes": {
"write_pets": "modify pets in your account",
"read_pets": "read your pets"
}
}
},
"responses":{
"FoundPets": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Pet"
}
}
}
},
"parameters":{
"petId": {
"in": "path",
"name": "petId",
"description": "ID of the pet",
"required": true,
"type": "integer",
"format": "int64"
}
},
"definitions": {
"Identified": {
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
},
"User": {
"allOf": [
{
"$ref": "#/definitions/Identified"
},
{
"properties": {
"username": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"phone": {
"type": "string"
},
"userStatus": {
"type": "integer",
"format": "int32",
"description": "User Status"
},
"pictures": {
"type": "array",
"items": {
"type": "string",
"format": "byte"
}
}
}
}
]
},
"Category": {
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
}
}
},
"Pet": {
"description" : "Test description",
"required": [
"name",
"photoUrls"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"category": {
"$ref": "#/definitions/Category"
},
"name": {
"type": "string",
"example": "doggie"
},
"photoUrls": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/Tag"
}
},
"status": {
"type": "string",
"description": "pet status in the store"
}
}
},
"Tag": {
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
}
}
},
"Order": {
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"petId": {
"type": "integer",
"format": "int64"
},
"quantity": {
"type": "integer",
"format": "int32"
},
"shipDate": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"description": "Order Status"
},
"complete": {
"type": "boolean"
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
swagger: '2.0'
info:
description: TODO
version: 0.0.1
title: API Spec
termsOfService: TODO
basePath: /v1
schemes:
- http
paths:
/aaa:
post:
tags:
- AAA
summary: TODO
description: TODO
operationId: aaa
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: TODO
required: true
schema:
$ref: '#/definitions/AAA'
responses:
'200':
description: Success
'405':
description: Invalid data
definitions:
AAA:
allOf:
- $ref: '#/definitions/BBB'
- type: object
properties:
propA:
type: string
propB:
type: string
BBB:
type: object
properties:
propA:
type: string