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 2316e40 Swagger UI enhancements. 2316e40 is described below commit 2316e4075b98c0a2e9c5186923423ecac0a4565f Author: JamesBognar <jamesbog...@apache.org> AuthorDate: Sun Apr 8 06:33:27 2018 -0700 Swagger UI enhancements. --- .../org/apache/juneau/dto/swagger/HeaderInfo.java | 12 ++-- .../java/org/apache/juneau/dto/swagger/Items.java | 39 +++++++++-- .../apache/juneau/dto/swagger/ParameterInfo.java | 10 ++- .../apache/juneau/dto/swagger/ResponseInfo.java | 10 ++- .../org/apache/juneau/dto/swagger/SchemaInfo.java | 16 +++-- .../apache/juneau/dto/swagger/ui/SwaggerUI.java | 81 +++++++++++++++++----- .../jsonschema/JsonSchemaSerializerSession.java | 17 ++--- .../examples/rest/petstore/PetStoreResource.java | 6 ++ .../apache/juneau/rest/BasicRestInfoProvider.java | 4 +- 9 files changed, 144 insertions(+), 51 deletions(-) diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java index 98bd49c..e4316fa 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java @@ -1142,23 +1142,27 @@ public class HeaderInfo extends SwaggerElement { * * @param swagger The swagger document containing the definitions. * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. + * @param maxDepth + * The maximum depth to resolve references. + * <br>After that level is reached, <code>$ref</code> references will be left alone. + * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. * @return * This object with references resolved. * <br>May or may not be the same object. */ - public HeaderInfo resolveRefs(Swagger swagger, Deque<String> refStack) { + public HeaderInfo resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) { if (ref != null) { - if (refStack.contains(ref) || refStack.size() > 2) + if (refStack.contains(ref) || refStack.size() >= maxDepth) return this; refStack.addLast(ref); - HeaderInfo r = swagger.findRef(ref, HeaderInfo.class).resolveRefs(swagger, refStack); + HeaderInfo r = swagger.findRef(ref, HeaderInfo.class).resolveRefs(swagger, refStack, maxDepth); refStack.removeLast(); return r; } if (items != null) - items = items.resolveRefs(swagger, refStack); + items = items.resolveRefs(swagger, refStack, maxDepth); return this; } diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java index 35c63bf..544c492 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java @@ -1115,26 +1115,55 @@ public class Items extends SwaggerElement { * * @param swagger The swagger document containing the definitions. * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. + * @param maxDepth + * The maximum depth to resolve references. + * <br>After that level is reached, <code>$ref</code> references will be left alone. + * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. * @return * This object with references resolved. * <br>May or may not be the same object. */ - public Items resolveRefs(Swagger swagger, Deque<String> refStack) { + public Items resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) { if (ref != null) { - if (refStack.contains(ref) || refStack.size() > 2) + if (refStack.contains(ref) || refStack.size() >= maxDepth) return this; refStack.addLast(ref); - Items r = swagger.findRef(ref, Items.class).resolveRefs(swagger, refStack); + Items r = swagger.findRef(ref, Items.class).resolveRefs(swagger, refStack, maxDepth); refStack.removeLast(); return r; } - + + set("properties", resolveRefs(get("properties"), swagger, refStack, maxDepth)); + if (items != null) - items = items.resolveRefs(swagger, refStack); + items = items.resolveRefs(swagger, refStack, maxDepth); set("example", null); return this; } + + /* Resolve references in extra attributes */ + private Object resolveRefs(Object o, Swagger swagger, Deque<String> refStack, int maxDepth) { + if (o instanceof ObjectMap) { + ObjectMap om = (ObjectMap)o; + String ref = om.getString("$ref"); + if (ref != null) { + if (refStack.contains(ref) || refStack.size() >= maxDepth) + return o; + refStack.addLast(ref); + Object o2 = swagger.findRef(ref, Object.class); + o2 = resolveRefs(o2, swagger, refStack, maxDepth); + refStack.removeLast(); + return o2; + } + for (Map.Entry<String,Object> e : om.entrySet()) + e.setValue(resolveRefs(e.getValue(), swagger, refStack, maxDepth)); + } + if (o instanceof ObjectList) + for (ListIterator<Object> li = ((ObjectList)o).listIterator(); li.hasNext();) + li.set(resolveRefs(li.next(), swagger, refStack, maxDepth)); + return o; + } } diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java index 3be783a..9034c5d 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java @@ -1582,17 +1582,21 @@ public class ParameterInfo extends SwaggerElement { * * @param swagger The swagger document containing the definitions. * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. + * @param maxDepth + * The maximum depth to resolve references. + * <br>After that level is reached, <code>$ref</code> references will be left alone. + * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. * @return * This object with references resolved. * <br>May or may not be the same object. */ - public ParameterInfo resolveRefs(Swagger swagger, Deque<String> refStack) { + public ParameterInfo resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) { if (schema != null) - schema = schema.resolveRefs(swagger, refStack); + schema = schema.resolveRefs(swagger, refStack, maxDepth); if (items != null) - items = items.resolveRefs(swagger, refStack); + items = items.resolveRefs(swagger, refStack, maxDepth); return this; } diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java index 6923368..5dd1da6 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java @@ -441,18 +441,22 @@ public class ResponseInfo extends SwaggerElement { * * @param swagger The swagger document containing the definitions. * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. + * @param maxDepth + * The maximum depth to resolve references. + * <br>After that level is reached, <code>$ref</code> references will be left alone. + * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. * @return * This object with references resolved. * <br>May or may not be the same object. */ - public ResponseInfo resolveRefs(Swagger swagger, Deque<String> refStack) { + public ResponseInfo resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) { if (schema != null) - schema = schema.resolveRefs(swagger, refStack); + schema = schema.resolveRefs(swagger, refStack, maxDepth); if (headers != null) for (Map.Entry<String,HeaderInfo> e : headers.entrySet()) - e.setValue(e.getValue().resolveRefs(swagger, refStack)); + e.setValue(e.getValue().resolveRefs(swagger, refStack, maxDepth)); return this; } diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java index 4ec8abb..8e970b2 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java @@ -1510,30 +1510,34 @@ public class SchemaInfo extends SwaggerElement { * * @param swagger The swagger document containing the definitions. * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. + * @param maxDepth + * The maximum depth to resolve references. + * <br>After that level is reached, <code>$ref</code> references will be left alone. + * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. * @return * This object with references resolved. * <br>May or may not be the same object. */ - public SchemaInfo resolveRefs(Swagger swagger, Deque<String> refStack) { + public SchemaInfo resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) { if (ref != null) { - if (refStack.contains(ref) || refStack.size() > 2) + if (refStack.contains(ref) || refStack.size() >= maxDepth) return this; refStack.addLast(ref); - SchemaInfo r = swagger.findRef(ref, SchemaInfo.class).resolveRefs(swagger, refStack); + SchemaInfo r = swagger.findRef(ref, SchemaInfo.class).resolveRefs(swagger, refStack, maxDepth); refStack.removeLast(); return r; } if (items != null) - items = items.resolveRefs(swagger, refStack); + items = items.resolveRefs(swagger, refStack, maxDepth); if (properties != null) for (Map.Entry<String,SchemaInfo> e : properties.entrySet()) - e.setValue(e.getValue().resolveRefs(swagger, refStack)); + e.setValue(e.getValue().resolveRefs(swagger, refStack, maxDepth)); if (additionalProperties != null) - additionalProperties = additionalProperties.resolveRefs(swagger, refStack); + additionalProperties = additionalProperties.resolveRefs(swagger, refStack, maxDepth); this.example = null; 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 d5e76c8..177e5ad 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 @@ -30,6 +30,42 @@ import org.apache.juneau.utils.*; */ public class SwaggerUI extends PojoSwap<Swagger,Div> { + //------------------------------------------------------------------------------------------------------------------- + // Configurable properties + //------------------------------------------------------------------------------------------------------------------- + + private static final String PREFIX = "SwaggerUI."; + + /** + * Configuration property: Resolve <code>$ref</code> references in schema up to the specified depth. + * + * <h5 class='section'>Property:</h5> + * <ul> + * <li><b>Name:</b> <js>"SwaggerUI.resolveRefsMaxDepth.i"</js> + * <li><b>Data type:</b> <code>Integer</code> + * <li><b>Default:</b> <code>1</code> + * <li><b>Session-overridable:</b> <jk>true</jk> + * </ul> + * + * <h5 class='section'>Description:</h5> + * <p> + * Defines the maximum recursive depth to resolve <code>$ref</code> variables in schema infos. + * <br>The default <code>1</code> means only resolve the first reference encountered. + * <br>A value of <code>0</code> disables reference resolution altogether. + * + * <h5 class='section'>Example:</h5> + * <p class='bcode'> + * <jc>// Resolve schema references up to 5 levels deep. + * <ja>@RestResource</ja>( + * properties={ + * <ja>@Property</ja>(name=<jsf>SWAGGERUI_resolveRefsMaxDepth</jsf>, value=<js>"5"</js>) + * } + * <jk>public class</jk> MyResource {...} + * </p> + */ + public static final String SWAGGERUI_resolveRefsMaxDepth = PREFIX + "resolveRefsMaxDepth.i"; + + static final ClasspathResourceManager RESOURCES = new ClasspathResourceManager(SwaggerUI.class); @Override @@ -37,10 +73,21 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { return new MediaType[] {MediaType.HTML}; } + private static final class Session { + final int resolveRefsMaxDepth; + final Swagger swagger; + + Session(BeanSession bs, Swagger swagger) { + this.swagger = swagger.copy(); + this.resolveRefsMaxDepth = bs.getProperty(SWAGGERUI_resolveRefsMaxDepth, Integer.class, 1); + System.err.println("resolveRefsMaxDepth=" + resolveRefsMaxDepth); + } + } + @Override - public Div swap(BeanSession session, Swagger s) throws Exception { + public Div swap(BeanSession beanSession, Swagger swagger) throws Exception { - s = s.copy(); + Session s = new Session(beanSession, swagger); Div outer = div( style(RESOURCES.getString("SwaggerUI.css")), @@ -51,8 +98,8 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { // Operations without tags are rendered first. outer.child(div()._class("tag-block tag-block-open").children(tagBlockContents(s, null))); - if (s.hasTags()) { - for (Tag t : s.getTags()) { + if (s.swagger.hasTags()) { + for (Tag t : s.swagger.getTags()) { Div tagBlock = div()._class("tag-block tag-block-open").children( tagBlockSummary(t), tagBlockContents(s, t) @@ -61,7 +108,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { } } - if (s.hasDefinitions()) { + if (s.swagger.hasDefinitions()) { Div modelBlock = div()._class("tag-block").children( modelsBlockSummary(), modelsBlockContents(s) @@ -73,10 +120,10 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { } // Creates the informational summary before the ops. - private Table header(Swagger s) { + private Table header(Session s) { Table table = table()._class("header"); - Info info = s.getInfo(); + Info info = s.swagger.getInfo(); if (info != null) { if (info.hasVersion()) @@ -108,7 +155,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { table.child(tr(th("License:"),td(child))); } - ExternalDocumentation ed = s.getExternalDocs(); + ExternalDocumentation ed = s.swagger.getExternalDocs(); if (ed != null) { Object child = ed.hasUrl() ? a(ed.getUrl(), ed.hasDescription() ? ed.getDescription() : ed.getUrl()) : ed.getDescription(); table.child(tr(th("Docs:"),td(child))); @@ -130,10 +177,10 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { } // Creates the contents under the "pet Everything about your Pets ext-link" header. - private Div tagBlockContents(Swagger s, Tag t) { + private Div tagBlockContents(Session s, Tag t) { Div tagBlockContents = div()._class("tag-block-contents"); - for (Map.Entry<String,Map<String,Operation>> e : s.getPaths().entrySet()) { + for (Map.Entry<String,Map<String,Operation>> e : s.swagger.getPaths().entrySet()) { String path = e.getKey(); for (Map.Entry<String,Operation> e2 : e.getValue().entrySet()) { String opName = e2.getKey(); @@ -146,7 +193,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { return tagBlockContents; } - private Div opBlock(Swagger s, String path, String opName, Operation op) { + private Div opBlock(Session s, String path, String opName, Operation op) { String opNameLc = op.isDeprecated() ? "deprecated" : opName.toLowerCase(); return div()._class("op-block op-block-closed " + opNameLc).children( @@ -163,7 +210,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { ).onclick("toggleOpBlock(this)"); } - private Div tableContainer(Swagger s, Operation op) { + private Div tableContainer(Session s, Operation op) { Div tableContainer = div()._class("table-container"); if (op.hasDescription()) @@ -220,7 +267,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { return tableContainer; } - private Div headers(Swagger s, ResponseInfo ri) { + private Div headers(Session s, ResponseInfo ri) { if (! ri.hasHeaders()) return null; @@ -246,7 +293,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { return headers; } - private Div examples(Swagger swagger, SchemaInfo si, Map<String,?> examples) { + private Div examples(Session s, SchemaInfo si, Map<String,?> examples) { if (si == null && examples == null) return null; @@ -258,7 +305,7 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { if (si != null) { select.child(option("model","model")); - div.child(div(si.copy().resolveRefs(swagger, new ArrayDeque<String>()))._class("model active").attr("data-name", "model")); + div.child(div(si.copy().resolveRefs(s.swagger, new ArrayDeque<String>(), s.resolveRefsMaxDepth))._class("model active").attr("data-name", "model")); } if (examples != null) { @@ -279,9 +326,9 @@ public class SwaggerUI extends PojoSwap<Swagger,Div> { } // Creates the contents under the "Model" header. - private Div modelsBlockContents(Swagger s) { + private Div modelsBlockContents(Session s) { Div modelBlockContents = div()._class("tag-block-contents"); - for (Map.Entry<String,ObjectMap> e : s.getDefinitions().entrySet()) + for (Map.Entry<String,ObjectMap> e : s.swagger.getDefinitions().entrySet()) modelBlockContents.child(modelBlock(e.getKey(), e.getValue())); return modelBlockContents; } diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaSerializerSession.java index e078030..c9032a7 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaSerializerSession.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaSerializerSession.java @@ -114,19 +114,14 @@ public class JsonSchemaSerializerSession extends JsonSerializerSession { boolean useDef = useBeanDefs && sType.isBean() && pNames == null; - if (useDef && defs.containsKey(getBeanDefId(sType))) { - ObjectMap schema = defs.get(getBeanDefId(sType)); - - // If we previously encountered this bean in a collection/array, then it may not have - // the example and description associated with it, so add it now. - if (! schema.containsKey("x-example")) - schema.appendIf(true, true, true, "x-example", getExample(sType, BEAN, exampleAdded)); - if (! schema.containsKey("description")) - schema.appendIf(true, true, true, "description", getDescription(sType, BEAN, exampleAdded)); - - return new ObjectMap().append("$ref", getBeanDefUri(sType)); + if (useDef) { + exampleAdded = false; + descriptionAdded = false; } + if (useDef && defs.containsKey(getBeanDefId(sType))) + return new ObjectMap().append("$ref", getBeanDefUri(sType)); + ObjectMap ds = defaultSchemas.get(sType.getInnerClass().getName()); if (ds != null && ds.containsKey("type")) return out.appendAll(ds); diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java index 16658f7..fe93b0a 100644 --- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java +++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java @@ -12,6 +12,8 @@ // *************************************************************************************************************************** package org.apache.juneau.examples.rest.petstore; +import static org.apache.juneau.dto.swagger.ui.SwaggerUI.*; + import java.util.*; import org.apache.juneau.internal.*; @@ -43,6 +45,10 @@ import org.apache.juneau.rest.widget.*; "source: $C{Source/gitHub}/org/apache/juneau/examples/rest/petstore/$R{servletClassSimple}.java" } ), + properties= { + // Resolve recursive references when showing schema info in the swagger. + @Property(name=SWAGGERUI_resolveRefsMaxDepth, value="99") + }, swagger="$F{PetStoreResource.json}" ) public class PetStoreResource extends BasicRestServletJena { diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java index 8f18b2b..887cc02 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java @@ -551,8 +551,8 @@ public class BasicRestInfoProvider implements RestInfoProvider { if (example == null) { ObjectMap schema = resolve(js, piri.getObjectMap("schema")); - if (schema != null) - example = schema.get("example"); + if (schema != null) + example = schema.getWithDefault("example", schema.get("x-example")); } if (example == null) -- To stop receiving notification emails like this one, please contact jamesbog...@apache.org.