You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/09/12 19:55:33 UTC
[juneau] branch master updated: Javadocs and Swagger UI tweaks.
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 2e478e4 Javadocs and Swagger UI tweaks.
2e478e4 is described below
commit 2e478e4df0ae14a963f7d705e0229e28bf93118d
Author: JamesBognar <ja...@apache.org>
AuthorDate: Wed Sep 12 15:55:05 2018 -0400
Javadocs and Swagger UI tweaks.
---
.../apache/juneau/dto/swagger/SwaggerElement.java | 16 ++
.../apache/juneau/dto/swagger/ui/SwaggerUI.java | 71 ++++--
.../src/main/java/org/apache/juneau/ObjectMap.java | 25 +++
.../org/apache/juneau/http/annotation/Query.java | 2 +-
juneau-doc/docs.txt | 1 +
.../juneau-rest-server.Swagger.Models.3.png | Bin 0 -> 59691 bytes
.../juneau-rest-server.Swagger.Operations.2.png | Bin 213265 -> 82846 bytes
juneau-doc/src/main/javadoc/overview.html | 243 +++++++++++++++++++--
.../07.juneau-rest-server/27.Swagger/03.Tags.html | 1 -
.../27.Swagger/04.Operations.html | 45 +++-
.../27.Swagger/05.Parameters.html | 85 ++++++-
.../27.Swagger/07.Responses.html | 1 +
.../27.Swagger/09.Models.html | 91 +++++++-
.../{07.Responses.html => 10.Stylesheet.html} | 12 +-
.../juneau-rest-server.Swagger.Models.3.png | Bin 0 -> 59691 bytes
.../juneau-rest-server.Swagger.Operations.2.png | Bin 213265 -> 82846 bytes
.../org/apache/juneau/rest/SwaggerGenerator.java | 81 ++++---
.../apache/juneau/rest/converters/Queryable.java | 23 +-
.../juneau/rest/annotation/PathAnnotationTest.java | 14 +-
19 files changed, 613 insertions(+), 98 deletions(-)
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SwaggerElement.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SwaggerElement.java
index f930749..19ee0eb 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SwaggerElement.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SwaggerElement.java
@@ -16,6 +16,7 @@ import static org.apache.juneau.internal.BeanPropertyUtils.*;
import java.util.*;
+import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.json.*;
import org.apache.juneau.utils.*;
@@ -158,6 +159,21 @@ public abstract class SwaggerElement {
return s;
}
+ /**
+ * Returns a copy of this swagger element as a modifiable map.
+ *
+ * <p>
+ * Each call produces a new map.
+ *
+ * @return A map containing all the values in this swagger element.
+ */
+ public ObjectMap asMap() {
+ ObjectMap m = new ObjectMap();
+ for (String s : keySet())
+ m.put(s, get(s, Object.class));
+ return m;
+ }
+
@Override /* Object */
public String toString() {
return JsonSerializer.DEFAULT.toString(this);
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ui/SwaggerUI.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ui/SwaggerUI.java
index e2901f1..f39a423 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ui/SwaggerUI.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ui/SwaggerUI.java
@@ -245,7 +245,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> {
Td parameterValue = td(
div(pi.getDescription())._class("description"),
- examples(s, pi.getSchema(), pi.getExamples())
+ examples(s, pi)
)._class("parameter-value");
parameters.child(tr(parameterKey, parameterValue));
@@ -267,7 +267,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> {
Td codeValue = td(
div(ri.getDescription())._class("description"),
- examples(s, ri.getSchema(), ri.getExamples()),
+ examples(s, ri),
headers(s, ri)
)._class("response-value");
@@ -304,28 +304,61 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> {
return headers;
}
- private Div examples(Session s, SchemaInfo si, Map<String,?> examples) {
- if (si == null && examples == null)
- return null;
+ private Div examples(Session s, ParameterInfo pi) {
+ boolean isBody = "body".equals(pi.getIn());
+
+ ObjectMap m = new ObjectMap();
+ if (isBody) {
+ SchemaInfo si = pi.getSchema();
+ if (si != null)
+ m.put("model", si.copy().resolveRefs(s.swagger, new ArrayDeque<String>(), s.resolveRefsMaxDepth));
+ } else {
+ ObjectMap om = pi
+ .copy()
+ .resolveRefs(s.swagger, new ArrayDeque<String>(), s.resolveRefsMaxDepth)
+ .asMap()
+ .keepAll("format","pattern","collectionFormat","maximum","minimum","multipleOf","maxLength","minLength","maxItems","minItems","allowEmptyValue","exclusiveMaximum","exclusiveMinimum","uniqueItems","items","default","enum");
+ m.put("model", om.isEmpty() ? i("none") : om);
+ }
- int count = (si == null ? 0 : 1) + (examples == null ? 0 : examples.entrySet().size());
- Select select = (Select)select().onchange("selectExample(this)")._class("example-select");
- if (count < 2)
- select.disabled(true);
- Div div = div(select)._class("examples");
+ Map<String,?> examples = pi.getExamples();
+ if (examples != null)
+ for (Map.Entry<String,?> e : examples.entrySet())
+ m.put(e.getKey(), e.getValue());
+
+ return examplesDiv(m);
+ }
+
+ private Div examples(Session s, ResponseInfo ri) {
+ SchemaInfo si = ri.getSchema();
+ ObjectMap m = new ObjectMap();
if (si != null) {
- select.child(option("model","model"));
- div.child(div(si.copy().resolveRefs(s.swagger, new ArrayDeque<String>(), s.resolveRefsMaxDepth))._class("model active").attr("data-name", "model"));
+ si = si.copy().resolveRefs(s.swagger, new ArrayDeque<String>(), s.resolveRefsMaxDepth);
+ m.put("model", si);
}
- if (examples != null) {
- for (Map.Entry<String,?> e : examples.entrySet()) {
- String name = e.getKey();
- String value = e.getValue().toString();
- select.child(option(name, name));
- div.child(div(value.replaceAll("\\n", "\n"))._class("example" + (si == null ? " active" : "")).attr("data-name", name));
- }
+ Map<String,?> examples = ri.getExamples();
+ if (examples != null)
+ for (Map.Entry<String,?> e : examples.entrySet())
+ m.put(e.getKey(), e.getValue());
+
+ return examplesDiv(m);
+ }
+
+ private Div examplesDiv(ObjectMap m) {
+ if (m.isEmpty())
+ return null;
+
+ Select select = (Select)select().disabled(m.size() < 2).onchange("selectExample(this)")._class("example-select");
+ Div div = div(select)._class("examples");
+
+ select.child(option("model","model"));
+ div.child(div(m.remove("model"))._class("model active").attr("data-name", "model"));
+
+ for (Map.Entry<String,Object> e : m.entrySet()) {
+ select.child(option(e.getKey(), e.getKey()));
+ div.child(div(e.getValue().toString().replaceAll("\\n", "\n"))._class("example").attr("data-name", e.getKey()));
}
return div;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
index 8e579b0..8c18aa1 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
@@ -1291,6 +1291,31 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
remove(k);
}
+ /**
+ * The opposite of {@link #removeAll(String...)}.
+ *
+ * <p>
+ * Discards all keys from this map that aren't in the specified list.
+ *
+ * @param keys The keys to keep.
+ * @return This map.
+ */
+ public ObjectMap keepAll(String...keys) {
+ for (Iterator<String> i = keySet().iterator(); i.hasNext();) {
+ boolean remove = true;
+ String key = i.next();
+ for (String k : keys) {
+ if (k.equals(key)) {
+ remove = false;
+ break;
+ }
+ }
+ if (remove)
+ i.remove();
+ }
+ return this;
+ }
+
@Override /* Map */
public boolean containsKey(Object key) {
if (super.containsKey(key))
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
index c18ed48..43f2802 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
@@ -871,7 +871,7 @@ public @interface Query {
* name=<js>"status"</js>,
* type=<js>"array"</js>,
* collectionFormat=<js>"csv"</js>,
- * example=<js>"AVALIABLE,PENDING"</js>
+ * example=<js>"AVAILABLE,PENDING"</js>
* )
* PetStatus[] status
* </p>
diff --git a/juneau-doc/docs.txt b/juneau-doc/docs.txt
index 902fd39..d7a2a5d 100644
--- a/juneau-doc/docs.txt
+++ b/juneau-doc/docs.txt
@@ -322,6 +322,7 @@ juneau-rest-server.Swagger.ParameterExamples = #juneau-rest-server.Swagger.Param
juneau-rest-server.Swagger.Parameters = #juneau-rest-server.Swagger.Parameters, Overview > juneau-rest-server > Swagger > Parameters
juneau-rest-server.Swagger.ResponseExamples = #juneau-rest-server.Swagger.ResponseExamples, Overview > juneau-rest-server > Swagger > Response Examples
juneau-rest-server.Swagger.Responses = #juneau-rest-server.Swagger.Responses, Overview > juneau-rest-server > Swagger > Responses
+juneau-rest-server.Swagger.Stylesheet = #juneau-rest-server.Swagger.Stylesheet, Overview > juneau-rest-server > Swagger > SwaggerUI.css
juneau-rest-server.Swagger.Tags = #juneau-rest-server.Swagger.Tags, Overview > juneau-rest-server > Swagger > Tags
juneau-rest-server.Transforms = #juneau-rest-server.Transforms, Overview > juneau-rest-server > Transforms
juneau-rest-server.URIs = #juneau-rest-server.URIs, Overview > juneau-rest-server > URIs
diff --git a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Models.3.png b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Models.3.png
new file mode 100644
index 0000000..01df721
Binary files /dev/null and b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Models.3.png differ
diff --git a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Operations.2.png b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Operations.2.png
index 014b981..9cbc361 100644
Binary files a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Operations.2.png and b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.Swagger.Operations.2.png differ
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index 1416d72..9d1c805 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -338,12 +338,13 @@
<li><p class='new'><a class='doclink' href='#juneau-rest-server.Swagger.BasicRestServlet'>BasicRestServlet</a></p>
<li><p class='new'><a class='doclink' href='#juneau-rest-server.Swagger.BasicSwaggerInfo'>Basic Swagger Info</a></p>
<li><p class='new'><a class='doclink' href='#juneau-rest-server.Swagger.Tags'>Tags</a></p>
- <li><p class='todo'><a class='doclink' href='#juneau-rest-server.Swagger.Operations'>Operations</a></p>
+ <li><p class='new'><a class='doclink' href='#juneau-rest-server.Swagger.Operations'>Operations</a></p>
<li><p class='todo'><a class='doclink' href='#juneau-rest-server.Swagger.Parameters'>Parameters</a></p>
<li><p class='todo'><a class='doclink' href='#juneau-rest-server.Swagger.ParameterExamples'>Parameter Examples</a></p>
<li><p class='todo'><a class='doclink' href='#juneau-rest-server.Swagger.Responses'>Responses</a></p>
<li><p class='todo'><a class='doclink' href='#juneau-rest-server.Swagger.ResponseExamples'>Response Examples</a></p>
- <li><p class='todo'><a class='doclink' href='#juneau-rest-server.Swagger.Models'>Models</a></p>
+ <li><p class='new'><a class='doclink' href='#juneau-rest-server.Swagger.Models'>Models</a></p>
+ <li><p class='new'><a class='doclink' href='#juneau-rest-server.Swagger.Stylesheet'>SwaggerUI.css</a></p>
</ol>
<li><p class=''><a class='doclink' href='#juneau-rest-server.HtmlDocAnnotation'>@HtmlDoc</a></p>
<ol>
@@ -18725,7 +18726,6 @@
}
}
],
-
</p>
<p>
The annotation-only approach is shown here:
@@ -18791,15 +18791,47 @@
<!-- ==================================================================================================== -->
-<h4 class='topic todo' onclick='toggle(this)'><a href='#juneau-rest-server.Swagger.Operations' id='juneau-rest-server.Swagger.Operations'>7.27.4 - Operations</a></h4>
+<h4 class='topic new' onclick='toggle(this)'><a href='#juneau-rest-server.Swagger.Operations' id='juneau-rest-server.Swagger.Operations'>7.27.4 - Operations</a></h4>
<div class='topic'><!-- START: 7.27.4 - juneau-rest-server.Swagger.Operations -->
<p>
- TODO
+ <ja>@RestMethod</ja>-annotated methods automatically get rendered as Swagger operations:
+</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.1.png'>
+<p>
+ The following shows the annotations defined on the <code>GET /pet</code> operation:
+</p>
+<h5 class='figure'>PetStoreResource.getPets()</h5>
+<p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ name=<jsf>GET</jsf>,
+ path=<js>"/pet"</js>,
+ summary=<js>"All pets in the store"</js>,
+ swagger=<ja>@MethodSwagger</ja>(
+ tags=<js>"pet"</js>,
+ ...
+ ),
+ ...
+ )
+ <jk>public</jk> Collection<Pet> getPets() <jk>throws</jk> NotAcceptable {
+ <jk>return</jk> <jsf>store</jsf>.getPets();
+ }
+</p>
+<p>
+ Methods marked as deprecated will show up as deprecated in the Swagger UI:
+</p>
+<h5 class='figure'>PetStoreResource.findPetsByTag()</h5>
+<p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ name=<jsf>GET</jsf>,
+ path=<js>"/pet/findByTags"</js>,
+ summary=<js>"Finds Pets by tags"</js>,
+ ...
+ )
+ <ja>@Deprecated</ja>
+ <jk>public</jk> Collection<Pet> findPetsByTags(...) {
+ ...
+ }
</p>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.1.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.2.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.3.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.4.png'>
</div><!-- END: 7.27.4 - juneau-rest-server.Swagger.Operations -->
<!-- ==================================================================================================== -->
@@ -18807,7 +18839,90 @@
<h4 class='topic todo' onclick='toggle(this)'><a href='#juneau-rest-server.Swagger.Parameters' id='juneau-rest-server.Swagger.Parameters'>7.27.5 - Parameters</a></h4>
<div class='topic'><!-- START: 7.27.5 - juneau-rest-server.Swagger.Parameters -->
<p>
- TODO
+ Expanding operations shows you a list of parameters:
+</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.2.png'>
+<p>
+ The <code>parameters</code> annotation is a free-form Simple JSON field that allows you to add arbitrary
+ parameter information to your resource.
+ In this case, we pull from a common field defined in the {@link org.apache.juneau.rest.converters.Queryable} converter class:
+</p>
+<p class='bpcode w800'>
+ <jk>public class</jk> Queryable <jk>implements</jk> RestConverter {
+
+ <jk>public static final</jk> String <jsf>SWAGGER_PARAMS</jsf>=<js>""</js>
+ + <js>"{"</js>
+ + <js>"in:'query',"</js>
+ + <js>"name:'s',"</js>
+ + <js>"description:'"</js>
+ + <js>"Search.\n"</js>
+ + <js>"Key/value pairs representing column names and search tokens.\n"</js>
+ + <js>"\\'*\\' and \\'?\\' can be used as meta-characters in string fields.\n"</js>
+ + <js>"\\'>\\', \\'>=\\', \\'<\\', and \\'<=\\' can be used as limits on numeric and date fields.\n"</js>
+ + <js>"Date fields can be matched with partial dates (e.g. \\'2018\\' to match any date in the year 2018)."</js>
+ + <js>"',"</js>
+ + <js>"type:'array',"</js>
+ + <js>"collectionFormat:'csv',"</js>
+ + <js>"x-examples:{example:'?s=Bill*,birthDate>2000'}"</js>
+ + <js>"},"</js>
+ + <js>"{"</js>
+ + <js>"in:'query',"</js>
+ + <js>"name:'v',"</js>
+ + <js>"description:'"</js>
+ + <js>"View.\n"</js>
+ + <js>"Column names to display."</js>
+ + <js>"',"</js>
+ + <js>"type:'array',"</js>
+ + <js>"collectionFormat:'csv',"</js>
+ + <js>"x-examples:{example:'?v=name,birthDate'}"</js>
+ + <js>"},"</js>
+ ...
+ ;
+ }
+</p>
+<p>
+ Note that this information could have also been defined in the Swagger JSON for the resource as well.
+</p>
+<p>
+ The Swagger can also be defined via annotations on parameters directly, as shown below:
+</p>
+<p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ ...
+ )
+ <jk>public</jk> Collection<Pet> getPets(
+ <ja>@Query</ja>(
+ name=<js>"s"</js>,
+ description={
+ <js>"Search."</js>,
+ <js>"Key/value pairs representing column names and search tokens."</js>,
+ <js>"'*' and '?' can be used as meta-characters in string fields."</js>,
+ <js>"'>', '>=', '<', and '<=' can be used as limits on numeric and date fields."</js>,
+ <js>"Date fields can be matched with partial dates (e.g. '2018' to match any date in the year 2018)."</js>
+ },
+ type=<js>"array"</js>,
+ collectionFormat=<js>"csv"</js>,
+ example=<js>"Bill*,birthDate>2000"</js>
+ )
+ String[] s,
+ <ja>@Query</ja>(
+ name=<js>"v"</js>,
+ description={
+ <js>"View."</js>,
+ <js>"Column names to display."</js>
+ },
+ type=<js>"array"</js>,
+ collectionFormat=<js>"csv"</js>,
+ example=<js>"name,birthDate"</js>
+ )
+ String[] v
+ ) <jk>throws</jk> NotAcceptable {
+ ...
+ }
+</p>
+<p>
+ <b>Note:</b> The <code>type</code> and <code>collectionFormat</code> values above are optional and auto-detected based on the
+ parameter class type if omitted.
</p>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Parameters.1.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Parameters.2.png'>
@@ -18831,6 +18946,7 @@
<p>
TODO
</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.3.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.1.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.2.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.3.png'>
@@ -18847,14 +18963,115 @@
<!-- ==================================================================================================== -->
-<h4 class='topic todo' onclick='toggle(this)'><a href='#juneau-rest-server.Swagger.Models' id='juneau-rest-server.Swagger.Models'>7.27.9 - Models</a></h4>
+<h4 class='topic new' onclick='toggle(this)'><a href='#juneau-rest-server.Swagger.Models' id='juneau-rest-server.Swagger.Models'>7.27.9 - Models</a></h4>
<div class='topic'><!-- START: 7.27.9 - juneau-rest-server.Swagger.Models -->
<p>
- TODO
+ The {@link org.apache.juneau.jsonschema.JsonSchemaGenerator#JSONSCHEMA_useBeanDefs} setting can be used to reduce the size of your
+ generated Swagger JSON files by creating model definitions for beans and referencing those definitions through <code>$ref</code> attributes.
+</p>
+<p>
+ By default, this flag is enabled when extending from {@link org.apache.juneau.rest.BasicRestServlet}:
+</p>
+<p class='bpcode w800'>
+ <jk>public abstract class</jk> BasicRestServlet <jk>extends</jk> RestServlet <jk>implements</jk> BasicRestConfig {
+
+ <ja>@RestMethod</ja>(name=<jsf>OPTIONS</jsf>, path=<js>"/*"</js>,
+ ...
+ flags={
+ <jc>// Use $ref references for bean definitions to reduce duplication in Swagger.</jc>
+ <jsf>JSONSCHEMA_useBeanDefs</jsf>
+ }
+ )
+ <jk>public</jk> Swagger getOptions(RestRequest req) {...}
+</p>
+<p>
+ In the Swagger UI, this causes bean definitions to show up in the Models section at the bottom of the page:
</p>
+<h5 class='figure'>Models section</h5>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.1.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.2.png'>
+<h5 class='figure'>Models section with Order bean expanded</h5>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.2.png'>
+<p>
+ In the generated Swagger JSON, embedded schema information for beans will be replaced with references such as the one shown below for the <code>Order</code> bean:
+</p>
+<p class='bpcode w800'>
+ {
+ <jok>"swagger"</jok>: <jov>"2.0"</jov>,
+ <jok>"paths"</jok>: {
+ <jok>"/store/order"</jok>: {
+ <jok>"get"</jok>: {
+ <jok>"operationId"</jok>: <jov>"getOrders"</jov>,
+ <jok>"summary"</jok>: <jov>"Petstore orders"</jov>,
+ <jok>"responses"</jok>: {
+ <jok>"200"</jok>: {
+ <jok>"description"</jok>: <jov>"OK"</jov>,
+ <jok>"schema"</jok>: {
+ <jok>"description"</jok>: <jov>"java.util.Collection<org.apache.juneau.examples.rest.petstore.Order>"</jov>,
+ <jok>"type"</jok>: <jov>"array"</jov>,
+ <jok>"items"</jok>: {
+ <jok>"$ref"</jok>: <jov>"#/definitions/Order"</jov>
+ }
+ },
+ ...
+ ...
+ ...
+ ...
+ ...
+ },
+ <jok>"definitions"</jok>: {
+ <jok>"Order"</jok>: {
+ <jok>"type"</jok>: <jov>"object"</jov>,
+ <jok>"properties"</jok>: {
+ <jok>"id"</jok>: {
+ <jok>"type"</jok>: <jov>"integer"</jov>,
+ <jok>"format"</jok>: <jov>"int64"</jov>
+ },
+ <jok>"petId": {
+ <jok>"type"</jok>: <jov>"integer"</jov>,
+ <jok>"format"</jok>: <jov>"int64"</jov>
+ },
+ <jok>"shipDate"</jok>: {
+ <jok>"type"</jok>: <jov>"string"</jov>
+ },
+ <jok>"status"</jok>: {
+ <jok>"type"</jok>: <jov>"string"</jov>,
+ <jok>"enum"</jok>: [
+ <jov>"PLACED"</jov>,
+ <jov>"APPROVED"</jov>,
+ <jov>"DELIVERED"</jov>
+ ]
+ }
+ },
+ <jok>"description"</jok>: <jov>"org.apache.juneau.examples.rest.petstore.Order"</jov>,
+ <jok>"example"</jok>: {
+ <jok>"id"</jok>: <jov>123</jov>,
+ <jok>"petId"</jok>: <jov>456</jov>,
+ <jok>"shipDate"</jok>: <jov>"2012-12-21"</jov>,
+ <jok>"status"</jok>: <jov>"APPROVED"</jov>
+ }
+ },
+ ...
+ }
+</p>
+<p>
+ Note that this does not affect how the information is rendered for that bean in the Swagger UI:
+</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.3.png'>
</div><!-- END: 7.27.9 - juneau-rest-server.Swagger.Models -->
+
+<!-- ==================================================================================================== -->
+
+<h4 class='topic new' onclick='toggle(this)'><a href='#juneau-rest-server.Swagger.Stylesheet' id='juneau-rest-server.Swagger.Stylesheet'>7.27.10 - SwaggerUI.css</a></h4>
+<div class='topic'><!-- START: 7.27.10 - juneau-rest-server.Swagger.Stylesheet -->
+<p>
+ The look-and-feel of the Swagger UI is controlled via a single CSS file: <code>SwaggerUI.css</code>.
+</p>
+<p>
+ In the microservice template, this file is located in the <code>files/htdocs/styles</code> directory.
+ It's a simple straightforward file consisting of less than 350 lines.
+ <br>This file can be modified to change the look-and-feel of your Swagger UI.
+</p>
+</div><!-- END: 7.27.10 - juneau-rest-server.Swagger.Stylesheet -->
</div><!-- END: 7.27 - juneau-rest-server.Swagger -->
<!-- ==================================================================================================== -->
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/03.Tags.html b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/03.Tags.html
index bd26f86..4c6566d 100644
--- a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/03.Tags.html
+++ b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/03.Tags.html
@@ -49,7 +49,6 @@
}
}
],
-
</p>
<p>
The annotation-only approach is shown here:
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/04.Operations.html b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/04.Operations.html
index 4c6f97b..aabe7f1 100644
--- a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/04.Operations.html
+++ b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/04.Operations.html
@@ -13,12 +13,45 @@
***************************************************************************************************************************/
-->
-{todo} Operations
+{new} Operations
<p>
- TODO
+ <ja>@RestMethod</ja>-annotated methods automatically get rendered as Swagger operations:
</p>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.1.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.2.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.3.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.4.png'>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.1.png'>
+<p>
+ The following shows the annotations defined on the <code>GET /pet</code> operation:
+</p>
+<h5 class='figure'>PetStoreResource.getPets()</h5>
+<p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ name=<jsf>GET</jsf>,
+ path=<js>"/pet"</js>,
+ summary=<js>"All pets in the store"</js>,
+ swagger=<ja>@MethodSwagger</ja>(
+ tags=<js>"pet"</js>,
+ ...
+ ),
+ ...
+ )
+ <jk>public</jk> Collection<Pet> getPets() <jk>throws</jk> NotAcceptable {
+ <jk>return</jk> <jsf>store</jsf>.getPets();
+ }
+</p>
+<p>
+ Methods marked as deprecated will show up as deprecated in the Swagger UI:
+</p>
+<h5 class='figure'>PetStoreResource.findPetsByTag()</h5>
+<p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ name=<jsf>GET</jsf>,
+ path=<js>"/pet/findByTags"</js>,
+ summary=<js>"Finds Pets by tags"</js>,
+ ...
+ )
+ <ja>@Deprecated</ja>
+ <jk>public</jk> Collection<Pet> findPetsByTags(...) {
+ ...
+ }
+</p>
+
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/05.Parameters.html b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/05.Parameters.html
index 61f7c3c..5987b72 100644
--- a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/05.Parameters.html
+++ b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/05.Parameters.html
@@ -16,7 +16,90 @@
{todo} Parameters
<p>
- TODO
+ Expanding operations shows you a list of parameters:
+</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.2.png'>
+<p>
+ The <code>parameters</code> annotation is a free-form Simple JSON field that allows you to add arbitrary
+ parameter information to your resource.
+ In this case, we pull from a common field defined in the {@link oajr.converters.Queryable} converter class:
+</p>
+<p class='bpcode w800'>
+ <jk>public class</jk> Queryable <jk>implements</jk> RestConverter {
+
+ <jk>public static final</jk> String <jsf>SWAGGER_PARAMS</jsf>=<js>""</js>
+ + <js>"{"</js>
+ + <js>"in:'query',"</js>
+ + <js>"name:'s',"</js>
+ + <js>"description:'"</js>
+ + <js>"Search.\n"</js>
+ + <js>"Key/value pairs representing column names and search tokens.\n"</js>
+ + <js>"\\'*\\' and \\'?\\' can be used as meta-characters in string fields.\n"</js>
+ + <js>"\\'>\\', \\'>=\\', \\'<\\', and \\'<=\\' can be used as limits on numeric and date fields.\n"</js>
+ + <js>"Date fields can be matched with partial dates (e.g. \\'2018\\' to match any date in the year 2018)."</js>
+ + <js>"',"</js>
+ + <js>"type:'array',"</js>
+ + <js>"collectionFormat:'csv',"</js>
+ + <js>"x-examples:{example:'?s=Bill*,birthDate>2000'}"</js>
+ + <js>"},"</js>
+ + <js>"{"</js>
+ + <js>"in:'query',"</js>
+ + <js>"name:'v',"</js>
+ + <js>"description:'"</js>
+ + <js>"View.\n"</js>
+ + <js>"Column names to display."</js>
+ + <js>"',"</js>
+ + <js>"type:'array',"</js>
+ + <js>"collectionFormat:'csv',"</js>
+ + <js>"x-examples:{example:'?v=name,birthDate'}"</js>
+ + <js>"},"</js>
+ ...
+ ;
+ }
+</p>
+<p>
+ Note that this information could have also been defined in the Swagger JSON for the resource as well.
+</p>
+<p>
+ The Swagger can also be defined via annotations on parameters directly, as shown below:
+</p>
+<p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ ...
+ )
+ <jk>public</jk> Collection<Pet> getPets(
+ <ja>@Query</ja>(
+ name=<js>"s"</js>,
+ description={
+ <js>"Search."</js>,
+ <js>"Key/value pairs representing column names and search tokens."</js>,
+ <js>"'*' and '?' can be used as meta-characters in string fields."</js>,
+ <js>"'>', '>=', '<', and '<=' can be used as limits on numeric and date fields."</js>,
+ <js>"Date fields can be matched with partial dates (e.g. '2018' to match any date in the year 2018)."</js>
+ },
+ type=<js>"array"</js>,
+ collectionFormat=<js>"csv"</js>,
+ example=<js>"Bill*,birthDate>2000"</js>
+ )
+ String[] s,
+ <ja>@Query</ja>(
+ name=<js>"v"</js>,
+ description={
+ <js>"View."</js>,
+ <js>"Column names to display."</js>
+ },
+ type=<js>"array"</js>,
+ collectionFormat=<js>"csv"</js>,
+ example=<js>"name,birthDate"</js>
+ )
+ String[] v
+ ) <jk>throws</jk> NotAcceptable {
+ ...
+ }
+</p>
+<p>
+ <b>Note:</b> The <code>type</code> and <code>collectionFormat</code> values above are optional and auto-detected based on the
+ parameter class type if omitted.
</p>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Parameters.1.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Parameters.2.png'>
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html
index b1a144b..50c9db2 100644
--- a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html
+++ b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html
@@ -18,6 +18,7 @@
<p>
TODO
</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Operations.3.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.1.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.2.png'>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.3.png'>
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/09.Models.html b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/09.Models.html
index 245d08e..64e5eb1 100644
--- a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/09.Models.html
+++ b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/09.Models.html
@@ -13,10 +13,97 @@
***************************************************************************************************************************/
-->
-{todo} Models
+{new} Models
<p>
- TODO
+ The {@link oaj.jsonschema.JsonSchemaGenerator#JSONSCHEMA_useBeanDefs} setting can be used to reduce the size of your
+ generated Swagger JSON files by creating model definitions for beans and referencing those definitions through <code>$ref</code> attributes.
</p>
+<p>
+ By default, this flag is enabled when extending from {@link oajr.BasicRestServlet}:
+</p>
+<p class='bpcode w800'>
+ <jk>public abstract class</jk> BasicRestServlet <jk>extends</jk> RestServlet <jk>implements</jk> BasicRestConfig {
+
+ <ja>@RestMethod</ja>(name=<jsf>OPTIONS</jsf>, path=<js>"/*"</js>,
+ ...
+ flags={
+ <jc>// Use $ref references for bean definitions to reduce duplication in Swagger.</jc>
+ <jsf>JSONSCHEMA_useBeanDefs</jsf>
+ }
+ )
+ <jk>public</jk> Swagger getOptions(RestRequest req) {...}
+</p>
+<p>
+ In the Swagger UI, this causes bean definitions to show up in the Models section at the bottom of the page:
+</p>
+<h5 class='figure'>Models section</h5>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.1.png'>
+<h5 class='figure'>Models section with Order bean expanded</h5>
<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.2.png'>
+<p>
+ In the generated Swagger JSON, embedded schema information for beans will be replaced with references such as the one shown below for the <code>Order</code> bean:
+</p>
+<p class='bpcode w800'>
+ {
+ <jok>"swagger"</jok>: <jov>"2.0"</jov>,
+ <jok>"paths"</jok>: {
+ <jok>"/store/order"</jok>: {
+ <jok>"get"</jok>: {
+ <jok>"operationId"</jok>: <jov>"getOrders"</jov>,
+ <jok>"summary"</jok>: <jov>"Petstore orders"</jov>,
+ <jok>"responses"</jok>: {
+ <jok>"200"</jok>: {
+ <jok>"description"</jok>: <jov>"OK"</jov>,
+ <jok>"schema"</jok>: {
+ <jok>"description"</jok>: <jov>"java.util.Collection<org.apache.juneau.examples.rest.petstore.Order>"</jov>,
+ <jok>"type"</jok>: <jov>"array"</jov>,
+ <jok>"items"</jok>: {
+ <jok>"$ref"</jok>: <jov>"#/definitions/Order"</jov>
+ }
+ },
+ ...
+ ...
+ ...
+ ...
+ ...
+ },
+ <jok>"definitions"</jok>: {
+ <jok>"Order"</jok>: {
+ <jok>"type"</jok>: <jov>"object"</jov>,
+ <jok>"properties"</jok>: {
+ <jok>"id"</jok>: {
+ <jok>"type"</jok>: <jov>"integer"</jov>,
+ <jok>"format"</jok>: <jov>"int64"</jov>
+ },
+ <jok>"petId": {
+ <jok>"type"</jok>: <jov>"integer"</jov>,
+ <jok>"format"</jok>: <jov>"int64"</jov>
+ },
+ <jok>"shipDate"</jok>: {
+ <jok>"type"</jok>: <jov>"string"</jov>
+ },
+ <jok>"status"</jok>: {
+ <jok>"type"</jok>: <jov>"string"</jov>,
+ <jok>"enum"</jok>: [
+ <jov>"PLACED"</jov>,
+ <jov>"APPROVED"</jov>,
+ <jov>"DELIVERED"</jov>
+ ]
+ }
+ },
+ <jok>"description"</jok>: <jov>"org.apache.juneau.examples.rest.petstore.Order"</jov>,
+ <jok>"example"</jok>: {
+ <jok>"id"</jok>: <jov>123</jov>,
+ <jok>"petId"</jok>: <jov>456</jov>,
+ <jok>"shipDate"</jok>: <jov>"2012-12-21"</jov>,
+ <jok>"status"</jok>: <jov>"APPROVED"</jov>
+ }
+ },
+ ...
+ }
+</p>
+<p>
+ Note that this does not affect how the information is rendered for that bean in the Swagger UI:
+</p>
+<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Models.3.png'>
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/10.Stylesheet.html
similarity index 73%
copy from juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html
copy to juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/10.Stylesheet.html
index b1a144b..931d102 100644
--- a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/07.Responses.html
+++ b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/10.Stylesheet.html
@@ -13,11 +13,13 @@
***************************************************************************************************************************/
-->
-{todo} Responses
+{new} SwaggerUI.css
<p>
- TODO
+ The look-and-feel of the Swagger UI is controlled via a single CSS file: <code>SwaggerUI.css</code>.
+</p>
+<p>
+ In the microservice template, this file is located in the <code>files/htdocs/styles</code> directory.
+ It's a simple straightforward file consisting of less than 350 lines.
+ <br>This file can be modified to change the look-and-feel of your Swagger UI.
</p>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.1.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.2.png'>
-<img class='bordered' style='width:900px' src='doc-files/juneau-rest-server.Swagger.Responses.3.png'>
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Models.3.png b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Models.3.png
new file mode 100644
index 0000000..01df721
Binary files /dev/null and b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Models.3.png differ
diff --git a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Operations.2.png b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Operations.2.png
index 014b981..9cbc361 100644
Binary files a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Operations.2.png and b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/27.Swagger/doc-files/juneau-rest-server.Swagger.Operations.2.png differ
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java
index 74c38d3..e6d20d5 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java
@@ -29,7 +29,6 @@ import org.apache.juneau.http.annotation.Contact;
import org.apache.juneau.http.annotation.Items;
import org.apache.juneau.http.annotation.License;
import org.apache.juneau.http.annotation.Tag;
-import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.jsonschema.*;
@@ -360,7 +359,10 @@ final class SwaggerGenerator {
if ((in == BODY || in == PATH) && ! param.containsKeyNotEmpty("required"))
param.put("required", true);
- addXExamples(sm, param, in.toString(), mp.getType());
+ if (in == BODY)
+ addBodyExamples(sm, param, false, mp.getType());
+ else
+ addParamExample(sm, param, in, mp.getType());
}
}
@@ -396,7 +398,7 @@ final class SwaggerGenerator {
ObjectMap om = responses.getObjectMap("200", true);
if (! om.containsKey("schema"))
om.appendSkipEmpty("schema", getSchema(om.getObjectMap("schema", true), m.getGenericReturnType()));
- addXExamples(sm, om, "ok", m.getGenericReturnType());
+ addBodyExamples(sm, om, true, m.getGenericReturnType());
}
// Finally, look for @ResponseHeader parameters defined on method.
@@ -719,7 +721,7 @@ final class SwaggerGenerator {
return om;
}
- private void addXExamples(RestJavaMethod sm, ObjectMap piri, String in, Type type) throws Exception {
+ private void addBodyExamples(RestJavaMethod sm, ObjectMap piri, boolean response, Type type) throws Exception {
String sex = piri.getString("x-example");
@@ -732,8 +734,6 @@ final class SwaggerGenerator {
if (isEmpty(sex))
return;
- boolean isOk = "ok".equals(in), isBody = "body".equals(in);
-
Object example = null;
if (isJson(sex)) {
example = JsonParser.DEFAULT.parse(sex, type);
@@ -744,47 +744,62 @@ final class SwaggerGenerator {
}
}
- String examplesKey = isOk ? "examples" : "x-examples"; // Parameters don't have an examples attribute.
+ String examplesKey = response ? "examples" : "x-examples"; // Parameters don't have an examples attribute.
ObjectMap examples = piri.getObjectMap(examplesKey);
if (examples == null)
examples = new ObjectMap();
- if (isOk || isBody) {
- List<MediaType> mediaTypes = isOk ? sm.getSerializers().getSupportedMediaTypes() : sm.getParsers().getSupportedMediaTypes();
-
- for (MediaType mt : mediaTypes) {
- if (mt != MediaType.HTML) {
- Serializer s2 = sm.getSerializers().getSerializer(mt);
- if (s2 != null) {
- SerializerSessionArgs args = new SerializerSessionArgs(null, req.getJavaMethod(), req.getLocale(), null, mt, null, req.isDebug() ? true : null, req.getUriContext(), true);
- try {
- String eVal = s2.createSession(args).serializeToString(example);
- examples.put(s2.getPrimaryMediaType().toString(), eVal);
- } catch (Exception e) {
- System.err.println("Could not serialize to media type ["+mt+"]: " + e.getLocalizedMessage()); // NOT DEBUG
- }
+ List<MediaType> mediaTypes = response ? sm.getSerializers().getSupportedMediaTypes() : sm.getParsers().getSupportedMediaTypes();
+
+ for (MediaType mt : mediaTypes) {
+ if (mt != MediaType.HTML) {
+ Serializer s2 = sm.getSerializers().getSerializer(mt);
+ if (s2 != null) {
+ SerializerSessionArgs args = new SerializerSessionArgs(null, req.getJavaMethod(), req.getLocale(), null, mt, null, req.isDebug() ? true : null, req.getUriContext(), true);
+ try {
+ String eVal = s2.createSession(args).serializeToString(example);
+ examples.put(s2.getPrimaryMediaType().toString(), eVal);
+ } catch (Exception e) {
+ System.err.println("Could not serialize to media type ["+mt+"]: " + e.getLocalizedMessage()); // NOT DEBUG
}
}
}
- } else {
- String paramName = piri.getString("name");
- String s = sm.partSerializer.createPartSession(req.getSerializerSessionArgs()).serialize(HttpPartType.valueOf(in.toUpperCase()), null, example);
- if ("query".equals(in))
- s = "?" + urlEncodeLax(paramName) + "=" + urlEncodeLax(s);
- else if ("formData".equals(in))
- s = paramName + "=" + s;
- else if ("header".equals(in))
- s = paramName + ": " + s;
- else if ("path".equals(in))
- s = sm.getPathPattern().replace("{"+paramName+"}", urlEncodeLax(s));
- examples.put("example", s);
}
if (! examples.isEmpty())
piri.put(examplesKey, examples);
}
+ private void addParamExample(RestJavaMethod sm, ObjectMap piri, RestParamType in, Type type) throws Exception {
+
+ String s = piri.getString("x-example");
+
+ if (isEmpty(s))
+ return;
+
+ ObjectMap examples = piri.getObjectMap("x-examples");
+ if (examples == null)
+ examples = new ObjectMap();
+
+ String paramName = piri.getString("name");
+
+ if (in == QUERY)
+ s = "?" + urlEncodeLax(paramName) + "=" + urlEncodeLax(s);
+ else if (in == FORM_DATA)
+ s = paramName + "=" + s;
+ else if (in == HEADER)
+ s = paramName + ": " + s;
+ else if (in == PATH)
+ s = sm.getPathPattern().replace("{"+paramName+"}", urlEncodeLax(s));
+
+ examples.put("example", s);
+
+ if (! examples.isEmpty())
+ piri.put("x-examples", examples);
+ }
+
+
private ObjectMap resolveRef(ObjectMap m) {
if (m == null)
return null;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Queryable.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Queryable.java
index b3cbfae..d8d7e63 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Queryable.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Queryable.java
@@ -85,7 +85,8 @@ public final class Queryable implements RestConverter {
+ "\\'>\\', \\'>=\\', \\'<\\', and \\'<=\\' can be used as limits on numeric and date fields.\n"
+ "Date fields can be matched with partial dates (e.g. \\'2018\\' to match any date in the year 2018)."
+ "',"
- + "schema:{type:'array',collectionFormat:'csv'},"
+ + "type:'array',"
+ + "collectionFormat:'csv',"
+ "x-examples:{example:'?s=Bill*,birthDate>2000'}"
+ "},"
+ "{"
@@ -94,8 +95,9 @@ public final class Queryable implements RestConverter {
+ "description:'"
+ "View.\n"
+ "Column names to display."
- +"',"
- + "schema:{type:'array',collectionFormat:'csv'},"
+ + "',"
+ + "type:'array',"
+ + "collectionFormat:'csv',"
+ "x-examples:{example:'?v=name,birthDate'}"
+ "},"
+ "{"
@@ -107,7 +109,8 @@ public final class Queryable implements RestConverter {
+ "Column names can be suffixed with \\'+\\' or \\'-\\' to indicate ascending or descending order.\n"
+ "The default is ascending order."
+ "',"
- + "schema:{type:'array',collectionFormat:'csv'},"
+ + "type:'array',"
+ + "collectionFormat:'csv',"
+ "x-examples:{example:'?o=name,birthDate-'}"
+ "},"
+ "{"
@@ -117,7 +120,7 @@ public final class Queryable implements RestConverter {
+ "Case-insensitive.\n"
+ "Flag for case-insensitive matching on the search parameters."
+ "',"
- + "schema:{type:'boolean'},"
+ + "type:'boolean',"
+ "x-examples:{example:'?i=true'}"
+ "},"
+ "{"
@@ -127,10 +130,10 @@ public final class Queryable implements RestConverter {
+ "Position.\n"
+ "Only return rows starting at the specified index position (zero-indexed).\n"
+ "Default is 0"
- +"',"
- + "schema:{type:'integer'},"
+ + "',"
+ + "type:'integer',"
+ "x-examples:{example:'?p=100'}"
- + "},"
+ + "},"
+ "{"
+ "in:'query',"
+ "name:'l',"
@@ -138,8 +141,8 @@ public final class Queryable implements RestConverter {
+ "Limit.\n"
+ "Only return the specified number of rows.\n"
+ "Default is 0 (meaning return all rows)."
- +"',"
- + "schema:{type:'integer'},"
+ + "',"
+ + "type:'integer',"
+ "x-examples:{example:'?l=100'}"
+ "}"
;
diff --git a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/PathAnnotationTest.java b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/PathAnnotationTest.java
index b4d3439..8da6f13 100644
--- a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/PathAnnotationTest.java
+++ b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/PathAnnotationTest.java
@@ -296,7 +296,7 @@ public class PathAnnotationTest {
description={"a","b"},
type="string",
_enum="a,b",
- example="'a'"
+ example="a"
)
public static class SA01 {
public SA01(String x) {}
@@ -338,7 +338,7 @@ public class PathAnnotationTest {
description={"a","b"},
type="string",
_enum="a,b",
- example="'a'"
+ example="a"
)
public static class SA03 {
public SA03(String x) {}
@@ -381,8 +381,8 @@ public class PathAnnotationTest {
assertEquals("a\nb", x.getDescription());
assertEquals("string", x.getType());
assertObjectEquals("['a','b']", x.getEnum());
- assertEquals("'a'", x.getExample());
- assertObjectEquals("{'in':'path',name:'P',type:'string',description:'a\\nb',required:true,'enum':['a','b'],'x-example':'\\'a\\'','x-examples':{example:'/basic/sa01'}}", x);
+ assertEquals("a", x.getExample());
+ assertObjectEquals("{'in':'path',name:'P',type:'string',description:'a\\nb',required:true,'enum':['a','b'],'x-example':'a','x-examples':{example:'/basic/a'}}", x);
}
@Test
public void sa02_Path_onPojo_api() throws Exception {
@@ -392,7 +392,7 @@ public class PathAnnotationTest {
assertEquals("string", x.getType());
assertObjectEquals("['a','b']", x.getEnum());
assertEquals("a", x.getExample());
- assertObjectEquals("{'in':'path',name:'P',type:'string',description:'a\\nb',required:true,'enum':['a','b'],'x-example':'a','x-examples':{example:'/api/sa02'}}", x);
+ assertObjectEquals("{'in':'path',name:'P',type:'string',description:'a\\nb',required:true,'enum':['a','b'],'x-example':'a','x-examples':{example:'/api/a'}}", x);
}
@Test
public void sa03_Path_onPojo_mixed() throws Exception {
@@ -401,8 +401,8 @@ public class PathAnnotationTest {
assertEquals("a\nb", x.getDescription());
assertEquals("string", x.getType());
assertObjectEquals("['a','b']", x.getEnum());
- assertEquals("'a'", x.getExample());
- assertObjectEquals("{'in':'path',name:'P',type:'string',description:'a\\nb',required:true,'enum':['a','b'],'x-example':'\\'a\\'','x-examples':{example:'/mixed/sa03'}}", x);
+ assertEquals("a", x.getExample());
+ assertObjectEquals("{'in':'path',name:'P',type:'string',description:'a\\nb',required:true,'enum':['a','b'],'x-example':'a','x-examples':{example:'/mixed/a'}}", x);
}
@Test
public void sa04_Path_onPojo_value() throws Exception {