Compare commits

...

6 Commits

Author SHA1 Message Date
Cas Eliëns
052b417fdb Identify location where query parameter examples are generated 2017-10-02 20:03:06 +02:00
Cas Eliëns
8f16457e49 Adjust query parameter test to expect new query parameter formatting 2017-10-02 20:02:24 +02:00
Cas Eliëns
2d8f46f5f6 Clean up code 2017-10-02 19:08:30 +02:00
Cas Eliëns
2886d30dec Begin simplifying example generation code 2017-07-27 11:36:16 +02:00
Cas Eliëns
282e74becb Create unit test for query parameter example generation 2017-07-24 09:56:06 +02:00
Cas Eliëns
164b1ad2a8 Begin identifying source of issue in #264 2017-07-20 16:33:37 +02:00
12 changed files with 506 additions and 65 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ build
/.classpath
/.project
/.settings/
/out/

View File

@@ -0,0 +1,14 @@
package io.github.swagger2markup;
/**
* swagger2markup (c) Duco Hosting
* Created by cas on 02-Oct-17.
*/
public enum ExampleType {
QUERY,
POST,
HEADER,
PATH,
BODY,
OTHER
}

View File

@@ -17,14 +17,12 @@
package io.github.swagger2markup.builder;
import com.google.common.collect.Ordering;
import io.github.swagger2markup.*;
import io.github.swagger2markup.markup.builder.LineSeparator;
import io.github.swagger2markup.markup.builder.MarkupLanguage;
import io.github.swagger2markup.model.PathOperation;
import io.swagger.models.HttpMethod;
import io.swagger.models.parameters.Parameter;
import org.apache.commons.configuration2.*;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
@@ -111,12 +109,12 @@ public class Swagger2MarkupConfigBuilder {
Optional<Pattern> headerPattern = swagger2MarkupProperties.getHeaderPattern(HEADER_REGEX);
config.headerPattern = headerPattern.orElse(null);
config.listDelimiterEnabled = swagger2MarkupProperties.getBoolean(LIST_DELIMITER_ENABLED, false);
config.listDelimiter = swagger2MarkupProperties.getString(LIST_DELIMITER, ",").charAt(0);
if (config.listDelimiterEnabled && configuration instanceof AbstractConfiguration) {
((AbstractConfiguration)configuration).setListDelimiterHandler(new DefaultListDelimiterHandler(config.listDelimiter));
((AbstractConfiguration) configuration).setListDelimiterHandler(new DefaultListDelimiterHandler(config.listDelimiter));
}
Configuration swagger2markupConfiguration = compositeConfiguration.subset(PROPERTIES_PREFIX);
@@ -218,19 +216,20 @@ public class Swagger2MarkupConfigBuilder {
config.separatedOperationsEnabled = true;
return this;
}
/**
* Allows properties to contain a list of elements delimited by a specified character.
*
* @return this builder
*/
public Swagger2MarkupConfigBuilder withListDelimiter() {
config.listDelimiterEnabled = true;
return this;
}
/**
* Specifies the list delimiter which should be used.
*
*
* @param delimiter the delimiter
* @return this builder
*/
@@ -763,7 +762,7 @@ public class Swagger2MarkupConfigBuilder {
public Character getListDelimiter() {
return listDelimiter;
}
@Override
public boolean isListDelimiterEnabled() {
return listDelimiterEnabled;

View File

@@ -345,15 +345,17 @@ public class PathOperationComponent extends MarkupComponent<PathOperationCompone
* @param operation the Swagger Operation
*/
private void buildExamplesSection(MarkupDocBuilder markupDocBuilder, PathOperation operation, List<PageBreakLocations> locations) {
// Generate examples
Map<String, Object> generatedRequestExampleMap = ExamplesUtil.generateRequestExampleMap(config.isGeneratedExamplesEnabled(), operation, definitions, definitionDocumentResolver, markupDocBuilder);
Map<String, Object> generatedResponseExampleMap = ExamplesUtil.generateResponseExampleMap(config.isGeneratedExamplesEnabled(), operation, definitions, definitionDocumentResolver, markupDocBuilder);
// Get page break settings
boolean beforeExampleRequestBreak = locations.contains(BEFORE_OPERATION_EXAMPLE_REQUEST);
boolean afterExampleRequestBreak = locations.contains(AFTER_OPERATION_EXAMPLE_REQUEST);
boolean beforeExampleResponseBreak = locations.contains(BEFORE_OPERATION_EXAMPLE_RESPONSE);
boolean afterExampleResponseBreak = locations.contains(AFTER_OPERATION_EXAMPLE_RESPONSE);
// Write examples
exampleMap(markupDocBuilder, generatedRequestExampleMap, labels.getLabel(EXAMPLE_REQUEST), labels.getLabel(REQUEST), beforeExampleRequestBreak, afterExampleRequestBreak);
exampleMap(markupDocBuilder, generatedResponseExampleMap, labels.getLabel(EXAMPLE_RESPONSE), labels.getLabel(RESPONSE), beforeExampleResponseBreak, afterExampleResponseBreak);
}
@@ -373,7 +375,6 @@ public class PathOperationComponent extends MarkupComponent<PathOperationCompone
Iterator<Map.Entry<String, JsonNode>> fieldsIterator = rootNode.fields();
if (!fieldsIterator.hasNext()) {
// rootNode contains a single example, no need to further iterate.
String example = Json.pretty(rootNode);
@@ -407,6 +408,10 @@ public class PathOperationComponent extends MarkupComponent<PathOperationCompone
} else if (entry.getKey().equals("path")) {
// Path shouldn't have quotes around it
markupDocBuilder.listingBlock(entry.getValue().toString());
} else if (entry.getKey().equals("query")) {
//TODO issue #264: print query parameters in table
// markupDocBuilder.listingBlock(entry.getValue().toString());
logger.debug("Skipping query parameter: " + entry.getValue().toString());
} else {
markupDocBuilder.listingBlock(Json.pretty(entry.getValue()), "json");
}

View File

@@ -0,0 +1,25 @@
package io.github.swagger2markup.internal.utils;
import io.github.swagger2markup.ExampleType;
/**
* swagger2markup (c) Duco Hosting
* Created by cas on 02-Oct-17.
*/
public class Example {
private ExampleType type;
private Object example;
public Example(ExampleType type, Object example) {
this.type = type;
this.example = example;
}
public ExampleType getType() {
return type;
}
public Object getExample() {
return example;
}
}

View File

@@ -100,62 +100,10 @@ public class ExamplesUtil {
for (Parameter parameter : parameters) {
Object example = null;
if (parameter instanceof BodyParameter) {
example = ((BodyParameter) parameter).getExamples();
if (example == null) {
Model schema = ((BodyParameter) parameter).getSchema();
if (schema instanceof RefModel) {
String simpleRef = ((RefModel) schema).getSimpleRef();
example = generateExampleForRefModel(generateMissingExamples, simpleRef, definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
} else if (generateMissingExamples) {
if (schema instanceof ComposedModel) {
//FIXME: getProperties() may throw NullPointerException
example = exampleMapForProperties(((ObjectType) ModelUtils.getType(schema, definitions, definitionDocumentResolver)).getProperties(), definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
} else if (schema instanceof ArrayModel) {
example = generateExampleForArrayModel((ArrayModel) schema, definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
} else {
example = schema.getExample();
if (example == null) {
example = exampleMapForProperties(schema.getProperties(), definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
}
}
}
}
example = generateBodyParameterExample(parameter, generateMissingExamples, definitions, definitionDocumentResolver, markupDocBuilder);
} else if (parameter instanceof AbstractSerializableParameter) {
if (generateMissingExamples) {
Object abstractSerializableParameterExample;
abstractSerializableParameterExample = ((AbstractSerializableParameter) parameter).getExample();
if (abstractSerializableParameterExample == null) {
abstractSerializableParameterExample = parameter.getVendorExtensions().get("x-example");
}
if (abstractSerializableParameterExample == null) {
Property item = ((AbstractSerializableParameter) parameter).getItems();
if (item != null) {
abstractSerializableParameterExample = item.getExample();
if (abstractSerializableParameterExample == null) {
abstractSerializableParameterExample = PropertyAdapter.generateExample(item, markupDocBuilder);
}
}
if (abstractSerializableParameterExample == null) {
abstractSerializableParameterExample = ParameterAdapter.generateExample((AbstractSerializableParameter) parameter);
}
}
if (parameter instanceof PathParameter) {
String pathExample = (String) examples.get("path");
pathExample = pathExample.replace('{' + parameter.getName() + '}', String.valueOf(abstractSerializableParameterExample));
example = pathExample;
} else {
example = abstractSerializableParameterExample;
}
if (parameter instanceof QueryParameter) {
//noinspection unchecked
@SuppressWarnings("unchecked")
Map<String, Object> queryExampleMap = (Map<String, Object>) examples.get("query");
if (queryExampleMap == null) {
queryExampleMap = new LinkedHashMap<>();
}
queryExampleMap.put(parameter.getName(), abstractSerializableParameterExample);
example = queryExampleMap;
}
example = generateAbstractSerializableParameterExample(parameter, examples, markupDocBuilder);
}
} else if (parameter instanceof RefParameter) {
String simpleRef = ((RefParameter) parameter).getSimpleRef();
@@ -169,6 +117,81 @@ public class ExamplesUtil {
return examples;
}
/**
* Generates example for a body parameter
*
* @param parameter Body paramteter to generate example for
* @param generateMissingExamples Should an example be generated if none is defined
* @param definitions the map of definitions
* @param definitionDocumentResolver definitions document resolver
* @param markupDocBuilder the markup builder
* @return Object containing example
*/
private static Object generateBodyParameterExample(Parameter parameter, Boolean generateMissingExamples, Map<String, Model> definitions, DocumentResolver definitionDocumentResolver, MarkupDocBuilder markupDocBuilder) {
Object example = ((BodyParameter) parameter).getExamples();
if (example == null) {
Model schema = ((BodyParameter) parameter).getSchema();
if (schema instanceof RefModel) {
String simpleRef = ((RefModel) schema).getSimpleRef();
example = generateExampleForRefModel(generateMissingExamples, simpleRef, definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
} else if (generateMissingExamples) {
if (schema instanceof ComposedModel) {
//FIXME: getProperties() may throw NullPointerException
example = exampleMapForProperties(((ObjectType) ModelUtils.getType(schema, definitions, definitionDocumentResolver)).getProperties(), definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
} else if (schema instanceof ArrayModel) {
example = generateExampleForArrayModel((ArrayModel) schema, definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
} else {
example = schema.getExample();
if (example == null) {
example = exampleMapForProperties(schema.getProperties(), definitions, definitionDocumentResolver, markupDocBuilder, new HashMap<>());
}
}
}
}
return example;
}
private static Object generateAbstractSerializableParameterExample(Parameter parameter, Map<String, Object> examples, MarkupDocBuilder markupDocBuilder) {
Object abstractSerializableParameterExample;
Object example;
abstractSerializableParameterExample = ((AbstractSerializableParameter) parameter).getExample();
if (abstractSerializableParameterExample == null) {
abstractSerializableParameterExample = parameter.getVendorExtensions().get("x-example");
}
if (abstractSerializableParameterExample == null) {
Property item = ((AbstractSerializableParameter) parameter).getItems();
if (item != null) {
abstractSerializableParameterExample = item.getExample();
if (abstractSerializableParameterExample == null) {
abstractSerializableParameterExample = PropertyAdapter.generateExample(item, markupDocBuilder);
}
}
if (abstractSerializableParameterExample == null) {
abstractSerializableParameterExample = ParameterAdapter.generateExample((AbstractSerializableParameter) parameter);
}
}
if (parameter instanceof PathParameter) {
String pathExample = (String) examples.get("path");
pathExample = pathExample.replace('{' + parameter.getName() + '}', String.valueOf(abstractSerializableParameterExample));
example = pathExample;
} else {
example = abstractSerializableParameterExample;
}
if (parameter instanceof QueryParameter) {
//TODO: #264 query parameters seem to be collected here
//noinspection unchecked
@SuppressWarnings("unchecked")
Map<String, Object> queryExampleMap = (Map<String, Object>) examples.get("query");
if (queryExampleMap == null) {
queryExampleMap = new LinkedHashMap<>();
}
queryExampleMap.put(parameter.getName(), abstractSerializableParameterExample);
example = queryExampleMap;
}
return example;
}
/**
* Generates an example object from a simple reference
*

View File

@@ -782,4 +782,29 @@ public class AsciidocConverterTest {
Path expectedFilesDirectory = Paths.get(AsciidocConverterTest.class.getResource("/expected/asciidoc/page_breaks").toURI());
DiffUtils.assertThatAllFilesAreEqual(expectedFilesDirectory, outputDirectory, "testWithPageBreaks.html");
}
@Test
public void testWithQueryParameters() throws IOException, URISyntaxException {
//Given
Path file = Paths.get(AsciidocConverterTest.class.getResource("/yaml/swagger_query_params.yaml").toURI());
Path outputDirectory = Paths.get("build/test/asciidoc/query_params");
FileUtils.deleteQuietly(outputDirectory.toFile());
//When
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withGeneratedExamples()
.build();
Swagger2MarkupConverter.from(file)
.withConfig(config)
.build()
.toFolder(outputDirectory);
//Then
String[] files = outputDirectory.toFile().list();
assertThat(files).hasSize(4).containsAll(expectedFiles);
Path expectedFilesDirectory = Paths.get(AsciidocConverterTest.class.getResource("/expected/asciidoc/query_params").toURI());
DiffUtils.assertThatAllFilesAreEqual(expectedFilesDirectory, outputDirectory, "textWithQueryParameters.html");
}
}

View File

@@ -0,0 +1,53 @@
[[_definitions]]
== Definitions
[[_category]]
=== Category
[options="header", cols=".^3,.^11,.^4"]
|===
|Name|Description|Schema
|**id** +
__optional__|**Example** : `0`|integer (int64)
|**name** +
__optional__|**Example** : `"string"`|string
|===
[[_pet]]
=== Pet
[options="header", cols=".^3,.^11,.^4"]
|===
|Name|Description|Schema
|**category** +
__optional__|**Example** : <<_category>>|<<_category,Category>>
|**id** +
__optional__|**Example** : `0`|integer (int64)
|**name** +
__required__|**Example** : `"doggie"`|string
|**photoUrls** +
__required__|**Example** : `[ "string" ]`|< string > array
|**status** +
__optional__|pet status in the store +
**Example** : `"string"`|enum (available, pending, sold)
|**tags** +
__optional__|**Example** : `[ "<<_tag>>" ]`|< <<_tag,Tag>> > array
|===
[[_tag]]
=== Tag
[options="header", cols=".^3,.^11,.^4"]
|===
|Name|Description|Schema
|**id** +
__optional__|**Example** : `0`|integer (int64)
|**name** +
__optional__|**Example** : `"string"`|string
|===

View File

@@ -0,0 +1,40 @@
= Swagger Petstore
[[_overview]]
== Overview
This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on http://swagger.io/irc/["irc.freenode.net, #swagger"]. For this sample, you can use the api key `special-key` to test the authorization filters.
=== Version information
[%hardbreaks]
__Version__ : 1.0.0
=== Contact information
[%hardbreaks]
__Contact Email__ : apiteam@swagger.io
=== License information
[%hardbreaks]
__License__ : Apache 2.0
__License URL__ : http://www.apache.org/licenses/LICENSE-2.0.html
__Terms of service__ : http://swagger.io/terms/
=== URI scheme
[%hardbreaks]
__Host__ : petstore.swagger.io
__BasePath__ : /v2
__Schemes__ : HTTP
=== Tags
* pet : Everything about your Pets
* store : Access to Petstore orders
* user : Operations about user

View File

@@ -0,0 +1,91 @@
[[_paths]]
== Paths
[[_findpetsbystatus]]
=== Finds Pets by status
....
GET /pet/findByStatus
....
==== Description
Multiple status values can be provided with comma separated strings
==== Parameters
[options="header", cols=".^2,.^3,.^9,.^4"]
|===
|Type|Name|Description|Schema
|**Query**|**status** +
__required__|Status values that need to be considered for filter|< enum (available, pending, sold) > array(multi)
|===
==== Responses
[options="header", cols=".^2,.^14,.^4"]
|===
|HTTP Code|Description|Schema
|**200**|successful operation|< <<_pet,Pet>> > array
|**400**|Invalid status value|No Content
|===
==== Produces
* `application/xml`
* `application/json`
==== Tags
* pet
==== Security
[options="header", cols=".^3,.^4,.^13"]
|===
|Type|Name|Scopes
|**oauth2**|**<<_petstore_auth,petstore_auth>>**|write:pets,read:pets
|===
==== Example HTTP request
===== Request path
----
/pet/findByStatus
----
===== Request query
|===
|Name|Value
|status|string
|===
==== Example HTTP response
===== Response 200
[source,json]
----
[ {
"id" : 0,
"category" : {
"id" : 0,
"name" : "string"
},
"name" : "doggie",
"photoUrls" : [ "string" ],
"tags" : [ {
"id" : 0,
"name" : "string"
} ],
"status" : "string"
} ]
----

View File

@@ -0,0 +1,26 @@
[[_securityscheme]]
== Security
[[_petstore_auth]]
=== petstore_auth
[%hardbreaks]
__Type__ : oauth2
__Flow__ : implicit
__Token URL__ : http://petstore.swagger.io/oauth/dialog
[options="header", cols=".^3,.^17"]
|===
|Name|Description
|write:pets|modify pets in your account
|read:pets|read your pets
|===
[[_api_key]]
=== api_key
[%hardbreaks]
__Type__ : apiKey
__Name__ : api_key
__In__ : HEADER

View File

@@ -0,0 +1,139 @@
# See issue swagger2markup#264 and #266
swagger: "2.0"
info:
description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters."
version: "1.0.0"
title: "Swagger Petstore"
termsOfService: "http://swagger.io/terms/"
contact:
email: "apiteam@swagger.io"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "petstore.swagger.io"
basePath: "/v2"
tags:
- name: "pet"
description: "Everything about your Pets"
externalDocs:
description: "Find out more"
url: "http://swagger.io"
- name: "store"
description: "Access to Petstore orders"
- name: "user"
description: "Operations about user"
externalDocs:
description: "Find out more about our store"
url: "http://swagger.io"
schemes:
- "http"
paths:
/pet/findByStatus:
get:
tags:
- "pet"
summary: "Finds Pets by status"
description: "Multiple status values can be provided with comma separated strings"
operationId: "findPetsByStatus"
produces:
- "application/xml"
- "application/json"
parameters:
- name: "status"
in: "query"
description: "Status values that need to be considered for filter"
required: true
type: "array"
items:
type: "string"
enum:
- "available"
- "pending"
- "sold"
default: "available"
collectionFormat: "multi"
responses:
200:
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/Pet"
400:
description: "Invalid status value"
security:
- petstore_auth:
- "write:pets"
- "read:pets"
securityDefinitions:
petstore_auth:
type: "oauth2"
authorizationUrl: "http://petstore.swagger.io/oauth/dialog"
flow: "implicit"
scopes:
write:pets: "modify pets in your account"
read:pets: "read your pets"
api_key:
type: "apiKey"
name: "api_key"
in: "header"
definitions:
Category:
type: "object"
properties:
id:
type: "integer"
format: "int64"
name:
type: "string"
xml:
name: "Category"
Tag:
type: "object"
properties:
id:
type: "integer"
format: "int64"
name:
type: "string"
xml:
name: "Tag"
Pet:
type: "object"
required:
- "name"
- "photoUrls"
properties:
id:
type: "integer"
format: "int64"
category:
$ref: "#/definitions/Category"
name:
type: "string"
example: "doggie"
photoUrls:
type: "array"
xml:
name: "photoUrl"
wrapped: true
items:
type: "string"
tags:
type: "array"
xml:
name: "tag"
wrapped: true
items:
$ref: "#/definitions/Tag"
status:
type: "string"
description: "pet status in the store"
enum:
- "available"
- "pending"
- "sold"
xml:
name: "Pet"